<?php

namespace App\Controller;

use App\Repository\EmailTrackingRepository;
use App\Service\AwsSesService;
use App\Service\DnsCheckService;
use App\Service\DnsInfraHelper;
use App\Service\MailcowService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;

/** @codeCoverageIgnore */
class DnsReportController extends AbstractController
{
    #[Route('/email/configuration/{token}', name: 'app_dns_report', methods: ['GET'])]
    public function __invoke(
        string $token,
        DnsCheckService $dnsCheck,
        AwsSesService $awsSes,
        MailcowService $mailcow,
        DnsInfraHelper $helper,
        EmailTrackingRepository $emailTrackingRepository,
        #[Autowire(service: 'dns_infra_cache')] CacheInterface $cache,
    ): Response {
        $tracking = $emailTrackingRepository->findOneBy(['messageId' => $token]);

        if (null === $tracking) {
            throw $this->createNotFoundException('Rapport introuvable.');
        }

        $cacheKey = 'dns_infra_check_'.$token;

        $data = $cache->get($cacheKey, function (ItemInterface $item) use ($dnsCheck, $awsSes, $mailcow, $helper): array {
            $item->expiresAfter(3600);

            $errors = [];
            $warnings = [];
            $successes = [];
            $domainResults = [];

            $cfRecordsByDomain = $helper->loadCloudflareRecords();

            foreach (DnsInfraHelper::DOMAINS as $domain) {
                $checks = [];
                $cfRecords = $cfRecordsByDomain[$domain] ?? [];

                $dnsCheck->checkSpf($domain, $checks, $errors, $warnings, $successes);
                $helper->enrichWithCloudflare($checks, $domain, 'SPF', 'TXT', $cfRecords);

                $dnsCheck->checkDmarc($domain, $checks, $errors, $successes);
                $helper->enrichWithCloudflare($checks, '_dmarc.'.$domain, 'DMARC', 'TXT', $cfRecords);

                $dnsCheck->checkMx($domain, DnsInfraHelper::EXPECTED_MX[$domain], $checks, $errors, $successes);
                $helper->enrichWithCloudflare($checks, $domain, 'MX', 'MX', $cfRecords);

                $dnsCheck->checkBounce($domain, $checks, $errors, $warnings, $successes);
                $helper->enrichLastCheck($checks, 'bounce.'.$domain, 'MX', $cfRecords);

                $dnsCheck->checkWhois($domain, $checks, $errors, $warnings, $successes);

                $this->checkAwsSes($domain, $awsSes, $dnsCheck, $helper, $checks, $errors, $successes, $cfRecords);
                $this->checkMailcow($domain, $mailcow, $helper, $checks, $errors, $warnings, $successes, $cfRecords);

                $domainResults[] = ['domain' => $domain, 'checks' => $checks];
            }

            return [
                'domainResults' => $domainResults,
                'errors' => $errors,
                'warnings' => $warnings,
                'successes' => $successes,
                'date' => (new \DateTimeImmutable())->format('c'),
            ];
        });

        return $this->render('dns_report/index.html.twig', [
            'domainResults' => $data['domainResults'],
            'errors' => $data['errors'],
            'warnings' => $data['warnings'],
            'successes' => $data['successes'],
            'date' => new \DateTimeImmutable($data['date']),
        ]);
    }

    /**
     * @param list<array>                $checks
     * @param list<string>               $errors
     * @param list<string>               $successes
     * @param list<array<string, mixed>> $cfRecords
     */
    private function checkAwsSes(string $domain, AwsSesService $awsSes, DnsCheckService $dnsCheck, DnsInfraHelper $helper, array &$checks, array &$errors, array &$successes, array $cfRecords): void
    {
        if (!$awsSes->isAvailable()) {
            return;
        }

        try {
            $verif = $awsSes->isDomainVerified($domain);
            $checks[] = DnsCheckService::check('AWS SES', 'Domaine', 'Success' === $verif ? 'ok' : 'error', $verif ?? 'Non verifie', 'Success', $verif ?? 'Absent');

            $dkim = $awsSes->getDkimStatus($domain);
            $dkimOk = $dkim['enabled'] && $dkim['verified'];
            $checks[] = DnsCheckService::check('AWS SES', 'DKIM statut', $dkimOk ? 'ok' : 'error', $dkimOk ? 'Active' : 'Inactive', 'Enabled + Verified', 'Enabled='.($dkim['enabled'] ? 'oui' : 'non').', Verified='.($dkim['verified'] ? 'oui' : 'non'));

            foreach ($dkim['tokens'] as $token) {
                $expectedCname = $token.'.dkim.amazonses.com';
                $dkimFqdn = $token.'._domainkey.'.$domain;
                $actualCname = $dnsCheck->getCnameRecord($dkimFqdn);
                $found = null !== $actualCname && str_contains($actualCname, 'dkim.amazonses.com');
                $checks[] = DnsCheckService::check('AWS SES', 'DKIM '.$token, $found ? 'ok' : 'error', $found ? 'Present' : 'Absent', $expectedCname, $actualCname ?? 'Non trouve');
                $helper->enrichLastCheck($checks, $dkimFqdn, 'CNAME', $cfRecords);
            }

            $mailFrom = $awsSes->getMailFromStatus($domain);
            if (null !== $mailFrom['mail_from_domain']) {
                $mfd = $mailFrom['mail_from_domain'];
                $checks[] = DnsCheckService::check('AWS SES', 'MAIL FROM', 'Success' === $mailFrom['mail_from_status'] ? 'ok' : 'error', $mailFrom['mail_from_status'] ?? '?', $mfd, $mailFrom['mail_from_status'] ?? '?');

                if (null !== $mailFrom['mx_expected']) {
                    $mxValues = [];
                    foreach ($dnsCheck->getMxRecords($mfd) as $mx) {
                        $mxValues[] = $mx['target'].' (pri: '.$mx['pri'].')';
                    }
                    $mxFound = !empty(array_filter($mxValues, fn ($v) => str_contains($v, 'amazonses.com')));
                    $checks[] = DnsCheckService::check('AWS SES', 'MAIL FROM MX', $mxFound ? 'ok' : 'error', $mxFound ? 'Present' : 'Absent', $mailFrom['mx_expected'], implode(', ', $mxValues) ?: 'Non trouve');
                    $helper->enrichLastCheck($checks, $mfd, 'MX', $cfRecords);
                }

                if (null !== $mailFrom['txt_expected']) {
                    $txtValue = $helper->getFirstTxtValue($mfd);
                    $txtFound = str_contains($txtValue, 'spf1');
                    $checks[] = DnsCheckService::check('AWS SES', 'MAIL FROM TXT', $txtFound ? 'ok' : 'error', $txtFound ? 'Present' : 'Absent', $mailFrom['txt_expected'], $txtValue ?: 'Non trouve');
                    $helper->enrichLastCheck($checks, $mfd, 'TXT', $cfRecords);
                }
            }
        } catch (\Throwable) {
        }
    }

    /**
     * @param list<array>                $checks
     * @param list<string>               $errors
     * @param list<string>               $warnings
     * @param list<string>               $successes
     * @param list<array<string, mixed>> $cfRecords
     */
    private function checkMailcow(string $domain, MailcowService $mailcow, DnsInfraHelper $helper, array &$checks, array &$errors, array &$warnings, array &$successes, array $cfRecords): void
    {
        if (!$mailcow->isAvailable()) {
            return;
        }

        try {
            $info = $mailcow->getDomainStatus($domain);
            if (null === $info) {
                return;
            }

            $checks[] = DnsCheckService::check('Mailcow', 'Domaine', $info['active'] ? 'ok' : 'error', $info['active'] ? "Actif ({$info['mailboxes']} boites)" : 'Desactive', 'Actif', $info['active'] ? 'Actif' : 'Desactive');
            foreach ($mailcow->getExpectedDnsRecords($domain) as $expected) {
                $found = $helper->checkDnsRecordExists($expected['type'], $expected['name'], $expected['content']);

                $label = $expected['type'].' '.$expected['name'];
                $digValue = $helper->getActualDnsValue($expected['type'], $expected['name']);
                $checks[] = DnsCheckService::check('Mailcow', $label, $found ? 'ok' : ($expected['optional'] ? 'warning' : 'error'), $found ? 'Present' : 'Absent', $expected['content'], $digValue ?: 'Non trouve');
                $helper->enrichLastCheck($checks, $expected['name'], $expected['type'], $cfRecords);
            }
        } catch (\Throwable) {
        }
    }
}
