<?php
namespace EasyChat\Library\Email;








final class EmailDeliverabilityAdvisor {
     
    private $dnsResolver;

    public function __construct(callable $dnsResolver = null) {
        $this->dnsResolver = $dnsResolver;
    }

    public function advise(array $config, array $outbound): array {
        $email = strtolower(trim((string)($config['sender_email'] ?? $config['email_address'] ?? '')));
        $domain = $this->domain($email);
        if ($domain === '') {
            return $this->result('unknown', 'dns_not_checked', 'Доставляемость ещё не проверена.', [], []);
        }

        $provider = EmailProviderCatalog::detect($email);
        $transport = strtolower((string)($outbound['transport'] ?? $config['outbound_transport'] ?? 'auto'));
        if ($provider !== null) {
            return $this->publicProviderAdvice($provider, $transport, $outbound);
        }

        return $this->ownedDomainAdvice($domain, $transport);
    }

    private function publicProviderAdvice(array $provider, string $transport, array $outbound): array {
        $name = (string)($provider['name'] ?? 'почтового сервиса');
        $direct = $transport === 'direct_smtp';
        $context=$this->transportContext($transport,$outbound);
        if ($direct) return $this->result(
            'ready',
            'provider_authentication_managed',
            'Используется SMTP ящика. SPF и DKIM обеспечивает ' . $name . '; окончательный результат проверяется по полученному письму.',
            [$this->check('spf','SPF','managed','Записью управляет '.$name.'.'),$this->check('dkim','DKIM','managed','Подпись добавляет SMTP '.$name.'.'),$this->check('dmarc','DMARC','managed','Политикой управляет владелец домена, менять DNS не нужно.')],[], $context
        );

        $transportLabel=$context['transport_label'];$addressDomain=(string)($provider['domains'][0]??'этого сервиса');
        $checks=[
            $this->check('sender_alignment','Адрес отправителя','warning','Адрес '.$addressDomain.' нельзя надёжно подтвердить через «'.$transportLabel.'». Используйте адрес на собственном домене.'),
            $this->check('spf','SPF','warning','Добавьте сервер выбранного способа отправки в единственную SPF-запись собственного домена.'),
            $this->check('dkim','DKIM','warning',$this->dkimMessage($transport)),
            $this->check('dmarc','DMARC','warning','Добавьте DMARC для собственного домена после настройки SPF и DKIM.'),
        ];
        $action=[];$alternatives=(array)($outbound['verified_alternatives']??array());
        if(in_array('direct_smtp',$alternatives,true))$action=['code'=>'select_direct_smtp','label'=>'Использовать проверенный SMTP ящика','transport'=>'direct_smtp'];
        $message='Используйте адрес на собственном домене и настройте SPF/DKIM для выбранного способа. Иначе ответы могут попадать в спам.';
        if(isset(((array)($outbound['rejected_transports']??array()))['direct_smtp']))$message.=' SMTP ящика уже проверен и недоступен, поэтому EasyChat не предлагает его повторно.';
        $result=$this->result('warning','provider_address_transport_mismatch',$message,$checks,$action,$context);
        $result['summary']='Письма отправляются, но адрес сервиса «'.$name.'» не подходит для способа «'.$transportLabel.'».';
        return $result;
    }

    private function ownedDomainAdvice(string $domain, string $transport): array {
        $mx = $this->records($domain, DNS_MX);
        $txt = $this->records($domain, DNS_TXT);
        $dmarcRecords = $this->records('_dmarc.' . $domain, DNS_TXT);
        $spf = array_values(array_filter($txt, static function ($record) {
            return stripos((string)($record['txt'] ?? ''), 'v=spf1') === 0;
        }));
        $dmarc = array_values(array_filter($dmarcRecords, static function ($record) {
            return stripos((string)($record['txt'] ?? ''), 'v=DMARC1') === 0;
        }));

        $checks = [];
        $checks[] = $this->check('mx', 'MX', $mx ? 'ready' : 'warning', $mx
            ? 'Почтовый домен принимает письма.'
            : 'MX не найден. Проверьте приём почты у владельца домена.');
        if (count($spf) === 1) {
            $checks[] = $this->check('spf', 'SPF', 'ready', 'Найдена одна SPF-запись. Проверьте, что она разрешает выбранный сервис отправки.');
        } elseif (count($spf) > 1) {
            $checks[] = $this->check('spf', 'SPF', 'error', 'Найдено несколько SPF-записей. Объедините их в одну — иначе проверка будет ошибочной.');
        } else {
            $checks[] = $this->check('spf', 'SPF', 'warning', 'SPF не найден. Добавьте разрешение для выбранного сервиса отправки.');
        }
        $checks[] = $this->check('dkim', 'DKIM', 'not_checked', $this->dkimMessage($transport));
        $checks[] = $this->check('dmarc', 'DMARC', $dmarc ? 'ready' : 'warning', $dmarc
            ? 'DMARC найден. Совпадение доменов проверится по реальному письму.'
            : 'DMARC не найден. Начните с политики наблюдения p=none и адреса для отчётов.');

        $problems = array_values(array_filter($checks, static function ($check) {
            return in_array((string)$check['status'], ['warning', 'error'], true);
        }));
        if ($problems) {
            return $this->result(
                'warning',
                'dns_action_required',
                'Подключение работает, но DNS домена стоит подготовить для стабильной доставки.',
                $checks,
                []
            );
        }
        return $this->result(
            'ready',
            'dns_baseline_ready',
            'Базовые DNS-записи найдены. DKIM и совпадение доменов подтвердятся по реальному письму.',
            $checks,
            []
        );
    }

    private function dkimMessage(string $transport): string {
        if ($transport === 'smtp_bz_api') return 'Добавьте DKIM-запись, которую выдаёт SMTP.BZ, и дождитесь подтверждения домена.';
        if ($transport === 'local_relay') return 'Включите DKIM-подпись на почтовом сервере хостинга и опубликуйте выданный селектор.';
        if ($transport === 'cms_mail') return 'Уточните фактический SMTP сайта и опубликуйте DKIM-селектор этого сервиса.';
        return 'Получите DKIM-селектор у SMTP-провайдера и опубликуйте его в DNS. Значение нельзя угадать автоматически.';
    }

    private function result(string $status, string $code, string $message, array $checks, array $action, array $context = []): array {
        return array_merge([
            'success'=>$status === 'ready',
            'status'=>$status,
            'code'=>$code,
            'summary'=>$this->summary($code),
            'message'=>$message,
            'checks'=>$checks,
            'action'=>$action,
        ],$context);
    }

    private function summary(string $code): string {
        $messages = [
            'provider_address_transport_mismatch'=>'Письма отправляются, но адрес почтового сервиса не подходит для выбранного способа.',
            'provider_authentication_managed'=>'Аутентификацией управляет почтовый сервис.',
            'dns_action_required'=>'Нужна настройка DNS домена.',
            'dns_baseline_ready'=>'Базовые DNS-записи найдены.',
            'dns_not_checked'=>'Доставляемость ещё не проверена.',
        ];
        return $messages[$code] ?? 'Проверьте рекомендации по доставляемости.';
    }

    private function transportContext(string $transport,array $outbound): array {
        $labels=['direct_smtp'=>'SMTP ящика','local_relay'=>'Почта хостинга','cms_mail'=>'Почта сайта','smtp_bz_api'=>'SMTP.BZ'];
        return ['transport'=>$transport,'transport_label'=>$labels[$transport]??'Выбранный способ','selection_mode'=>(string)($outbound['selection_mode']??'manual')];
    }

    private function check(string $code, string $label, string $status, string $message): array {
        return ['code'=>$code, 'label'=>$label, 'status'=>$status, 'message'=>$message];
    }

    private function domain(string $email): string {
        $at = strrpos($email, '@');
        if ($at === false) return '';
        $domain = substr($email, $at + 1);
        return filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) ? $domain : '';
    }

    private function records(string $host, int $type): array {
        if ($this->dnsResolver !== null) {
            $records = call_user_func($this->dnsResolver, $host, $type);
            return is_array($records) ? array_slice($records, 0, 20) : [];
        }
        set_error_handler(static function () { return true; });
        try { $records = dns_get_record($host, $type); }
        finally { restore_error_handler(); }
        return is_array($records) ? array_slice($records, 0, 20) : [];
    }
}
