<?php

namespace App\Service;

/**
 * Verification DNS pour les services E-Mail (reception) et E-Mailer (envoi SES).
 *
 * @codeCoverageIgnore Appels DNS externes (dig, MX, DKIM, DMARC, AWS SES)
 */
class EsyMailDnsService
{
    public function __construct(
        private DnsCheckService $dnsCheck,
        private AwsSesService $awsSes,
        private EsyMailService $esyMail,
    ) {
    }

    /**
     * Verifie la config DNS E-Mail (reception) pour un domaine.
     *
     * @return array{ok: bool, mx: bool, spf: bool, dkim: bool, dmarc: bool, details: array<string, string>}
     */
    public function checkDnsEsyMail(string $domain): array
    {
        $result = ['ok' => false, 'mx' => false, 'spf' => false, 'dkim' => false, 'dmarc' => false, 'details' => []];
        $mailHostname = $this->esyMail->getMailHostname();

        $result = $this->checkMx($domain, $mailHostname, $result);
        $result = $this->checkSpf($domain, $mailHostname, $result);
        $result = $this->checkDkim($domain, $result);
        $result = $this->checkDmarc($domain, $result);

        $result['ok'] = $result['mx'] && $result['spf'] && $result['dkim'] && $result['dmarc'];

        return $result;
    }

    /**
     * Verifie la config DNS E-Mailer (envoi AWS SES) pour un domaine.
     *
     * @return array{ok: bool, ses_verified: bool, ses_dkim: bool, spf_ses: bool, mail_from: bool, details: array<string, string>}
     */
    public function checkDnsEsyMailer(string $domain): array
    {
        $result = ['ok' => false, 'ses_verified' => false, 'ses_dkim' => false, 'spf_ses' => false, 'mail_from' => false, 'details' => []];

        if (!$this->awsSes->isAvailable()) {
            $result['details']['error'] = 'AWS SES non configure';

            return $result;
        }

        $verif = $this->awsSes->isDomainVerified($domain);
        $result['ses_verified'] = 'Success' === $verif;
        $result['details']['ses_verified'] = $verif ?? 'Non verifie';

        $dkim = $this->awsSes->getDkimStatus($domain);
        $result['ses_dkim'] = $dkim['enabled'] && $dkim['verified'];
        $result['details']['ses_dkim'] = ($dkim['enabled'] ? 'Enabled' : 'Disabled').', '.($dkim['verified'] ? 'Verified' : 'Not verified');

        $this->checkSpfSes($domain, $result);

        $mailFrom = $this->awsSes->getMailFromStatus($domain);
        $result['mail_from'] = 'Success' === ($mailFrom['mail_from_status'] ?? '');
        $result['details']['mail_from'] = ($mailFrom['mail_from_domain'] ?? 'Non configure').' ('.($mailFrom['mail_from_status'] ?? '?').')';

        $result['ok'] = $result['ses_verified'] && $result['ses_dkim'] && $result['spf_ses'] && $result['mail_from'];

        return $result;
    }

    /**
     * @param array<string, mixed> $result
     *
     * @return array<string, mixed>
     */
    private function checkMx(string $domain, string $mailHostname, array $result): array
    {
        $mxRecords = $this->dnsCheck->getMxRecords($domain);
        foreach ($mxRecords as $mx) {
            if ('' !== $mailHostname && str_contains($mx['target'], $mailHostname)) {
                $result['mx'] = true;
                break;
            }
        }
        $result['details']['mx'] = implode(', ', array_map(fn ($mx) => $mx['target'], $mxRecords)) ?: 'Aucun';

        return $result;
    }

    /**
     * @param array<string, mixed> $result
     *
     * @return array<string, mixed>
     */
    private function checkSpf(string $domain, string $mailHostname, array $result): array
    {
        $spfOutput = $this->dnsCheck->dig($domain, 'TXT');
        foreach (explode("\n", $spfOutput) as $line) {
            if (preg_match('/IN\s+TXT\s+"(v=spf1[^"]+)"/', $line, $m)) {
                $spf = str_replace('" "', '', $m[1]);
                if (str_contains($spf, $mailHostname) || str_contains($spf, 'include:_spf')) {
                    $result['spf'] = true;
                }
                $result['details']['spf'] = $spf;
                break;
            }
        }

        return $result;
    }

    /**
     * @param array<string, mixed> $result
     *
     * @return array<string, mixed>
     */
    private function checkDkim(string $domain, array $result): array
    {
        $dkimFqdn = 'dkim._domainkey.'.$domain;
        $dkimTxt = $this->dnsCheck->getDkimTxtRecord($dkimFqdn);
        $dkimCname = $this->dnsCheck->getCnameRecord($dkimFqdn);
        $result['dkim'] = null !== $dkimTxt || null !== $dkimCname;
        $result['details']['dkim'] = $dkimTxt ?? $dkimCname ?? 'Non trouve';

        return $result;
    }

    /**
     * @param array<string, mixed> $result
     *
     * @return array<string, mixed>
     */
    private function checkDmarc(string $domain, array $result): array
    {
        $dmarcOutput = $this->dnsCheck->dig('_dmarc.'.$domain, 'TXT');
        foreach (explode("\n", $dmarcOutput) as $line) {
            if (preg_match('/IN\s+TXT\s+"(v=DMARC1[^"]+)"/', $line, $m)) {
                $result['dmarc'] = true;
                $result['details']['dmarc'] = str_replace('" "', '', $m[1]);
                break;
            }
        }

        return $result;
    }

    /**
     * @param array<string, mixed> $result
     */
    private function checkSpfSes(string $domain, array &$result): void
    {
        $spfOutput = $this->dnsCheck->dig($domain, 'TXT');
        foreach (explode("\n", $spfOutput) as $line) {
            if (preg_match('/IN\s+TXT\s+"(v=spf1[^"]+)"/', $line, $m)) {
                $spf = str_replace('" "', '', $m[1]);
                if (str_contains($spf, 'amazonses.com')) {
                    $result['spf_ses'] = true;
                }
                $result['details']['spf'] = $spf;
                break;
            }
        }
    }
}
