<?php

namespace App\Controller\Admin;

use App\Entity\AdvertPayment;
use App\Entity\Facture;
use App\Service\ComptaExportService;
use App\Service\ComptaHelperService;
use App\Service\DocuSealService;
use App\Service\MailerService;
use App\Service\Pdf\ComptaPdf;
use App\Service\Pdf\RapportFinancierPdf;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/admin/comptabilite', name: 'app_admin_comptabilite_')]
#[IsGranted('ROLE_ROOT')]
class ComptabiliteController extends AbstractController
{
    private const CONTENT_DISPOSITION_PREFIX = 'attachment; filename="';

    public function __construct(
        private EntityManagerInterface $em,
        private KernelInterface $kernel,
        #[Autowire(env: 'DOCUSEAL_URL')]
        private string $docuSealUrl,
        private ComptaExportService $exportService,
        private ComptaHelperService $helper,
    ) {
    }

    #[Route('', name: 'index')]
    public function index(): Response
    {
        return $this->render('admin/comptabilite/index.html.twig');
    }

    /**
     * Export Journal des ventes (compatible SAGE).
     */
    #[Route('/export/journal-ventes', name: 'export_journal_ventes')]
    public function exportJournalVentes(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildJournalVentesData($from, $to),
            'journal_ventes_'.$from->format('Ymd').'_'.$to->format('Ymd'),
            $format,
        );
    }

    /**
     * Export Grand livre clients (compatible SAGE).
     */
    #[Route('/export/grand-livre', name: 'export_grand_livre')]
    public function exportGrandLivre(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildGrandLivreData($from, $to),
            'grand_livre_clients_'.$from->format('Ymd').'_'.$to->format('Ymd'),
            $format,
        );
    }

    /**
     * Export FEC - Fichier des Ecritures Comptables (norme legale francaise, compatible SAGE).
     */
    #[Route('/export/fec', name: 'export_fec')]
    public function exportFec(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildFecData($from, $to),
            'FEC_'.$from->format('Ymd').'_'.$to->format('Ymd'),
            $format,
        );
    }

    /**
     * Export balance agee (creances clients).
     */
    #[Route('/export/balance-agee', name: 'export_balance_agee')]
    public function exportBalanceAgee(Request $request): Response
    {
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildBalanceAgeeData(),
            'balance_agee_'.(new \DateTimeImmutable())->format('Ymd'),
            $format,
        );
    }

    /**
     * Export liste des reglements (paiements recus).
     */
    #[Route('/export/reglements', name: 'export_reglements')]
    public function exportReglements(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildReglementsData($from, $to),
            'reglements_'.$from->format('Ymd').'_'.$to->format('Ymd'),
            $format,
        );
    }

    /**
     * Export echeanciers (compatible SAGE).
     */
    #[Route('/export/echeanciers', name: 'export_echeanciers')]
    public function exportEcheanciers(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildEcheancierData($from, $to),
            'echeanciers_'.$from->format('Ymd').'_'.$to->format('Ymd'),
            $format,
        );
    }

    /**
     * Export PDF d'un type donne (telecharge directement).
     */
    #[Route('/export-pdf/{type}', name: 'export_pdf', requirements: ['type' => 'journal-ventes|grand-livre|fec|balance-agee|reglements|commissions-stripe|couts-services|echeanciers'])]
    public function exportPdf(string $type, Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);

        $titleMap = $this->getExportTitleMap();

        $rows = $this->exportService->getExportData($type, $from, $to);
        $title = $titleMap[$type] ?? $type;
        $periodFrom = $from->format(ComptaExportService::DATE_FORMAT_FR);
        $periodTo = $to->format(ComptaExportService::DATE_FORMAT_FR);

        $pdf = new ComptaPdf($this->kernel, $title, $periodFrom, $periodTo);
        $pdf->setData($rows);
        $pdf->generate();

        $filename = str_replace('-', '_', $type).'_'.$from->format('Ymd').'_'.$to->format('Ymd').'.pdf';

        return new Response($pdf->Output('S'), 200, [
            'Content-Type' => 'application/pdf',
            'Content-Disposition' => self::CONTENT_DISPOSITION_PREFIX.$filename.'"',
        ]);
    }

    /**
     * Export PDF + envoi a DocuSeal pour signature, puis redirection vers DocuSeal.
     */
    #[Route('/export-pdf/{type}/sign', name: 'export_pdf_sign', requirements: ['type' => 'journal-ventes|grand-livre|fec|balance-agee|reglements|commissions-stripe|couts-services|echeanciers'])]
    public function exportPdfSign(string $type, Request $request, DocuSealService $docuSeal): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);

        $titleMap = $this->getExportTitleMap();

        $rows = $this->exportService->getExportData($type, $from, $to);
        $title = $titleMap[$type] ?? $type;
        $periodFrom = $from->format(ComptaExportService::DATE_FORMAT_FR);
        $periodTo = $to->format(ComptaExportService::DATE_FORMAT_FR);

        $pdf = new ComptaPdf($this->kernel, $title, $periodFrom, $periodTo);
        $pdf->setData($rows);
        $pdf->generate();

        // Sauvegarder le PDF temporairement
        $filename = str_replace('-', '_', $type).'_'.$from->format('Ymd').'_'.$to->format('Ymd');
        $tmpPath = sys_get_temp_dir().'/'.$filename.'.pdf';
        $pdf->Output($tmpPath, 'F');

        $pdfContent = file_get_contents($tmpPath);
        @unlink($tmpPath);

        /** @var \App\Entity\User $user */
        $user = $this->getUser();
        $redirectUrl = $this->generateUrl('app_admin_comptabilite_sign_callback', [
            'type' => $type,
            'period' => $request->query->getString('period', 'current'),
            'from' => $request->query->getString('from', ''),
            'to' => $request->query->getString('to', ''),
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $submitterId = $docuSeal->sendComptaForSignature(
            $pdfContent,
            $title.' - '.$periodFrom.' au '.$periodTo,
            $user->getEmail(),
            $user->getFullName(),
            ['export_type' => $type, 'period_from' => $from->format('Y-m-d'), 'period_to' => $to->format('Y-m-d')],
            $redirectUrl,
        );

        if (null !== $submitterId) {
            $slug = $docuSeal->getSubmitterSlug($submitterId);
            if (null !== $slug) {
                $request->getSession()->set('compta_submitter_id', $submitterId);

                return $this->redirect(rtrim($this->docuSealUrl, '/').'/s/'.$slug);
            }
        }

        $this->addFlash('error', 'Erreur lors de la creation de la signature DocuSeal.');

        return $this->redirectToRoute('app_admin_comptabilite_index');
    }

    /**
     * Callback apres signature DocuSeal : telecharge le PDF signe et l'envoie par email.
     */
    #[Route('/sign-callback/{type}', name: 'sign_callback', requirements: ['type' => 'journal-ventes|grand-livre|fec|balance-agee|reglements|commissions-stripe|couts-services|echeanciers|rapport-financier'])]
    public function signCallback(string $type, Request $request, DocuSealService $docuSeal, MailerService $mailer, \Twig\Environment $twig): Response
    {
        $submitterId = $request->getSession()->get('compta_submitter_id');

        if (null === $submitterId) {
            $this->addFlash('warning', 'Session de signature expiree.');

            return $this->redirectToRoute('app_admin_comptabilite_index');
        }

        $request->getSession()->remove('compta_submitter_id');
        $submitterData = $docuSeal->getSubmitterData((int) $submitterId);
        $attachments = $this->downloadSignedDocuments($submitterData, $type);

        /** @var \App\Entity\User $user */
        $user = $this->getUser();

        if (!empty($attachments)) {
            $this->sendSignedDocumentEmail($submitterData, $type, $user, $attachments, $mailer, $twig);
            $this->addFlash('success', 'Document signe et envoye par email a '.$user->getEmail());
        } else {
            $this->addFlash('warning', 'Signature effectuee mais le document n\'a pas pu etre telecharge. Reessayez.');
        }

        return $this->redirectToRoute('app_admin_comptabilite_index');
    }

    /**
     * Export commissions Stripe sur la periode.
     */
    #[Route('/export/commissions-stripe', name: 'export_commissions_stripe')]
    public function exportCommissionsStripe(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildCommissionsStripeData($from, $to),
            'commissions_stripe_'.$from->format('Ymd').'_'.$to->format('Ymd'),
            $format,
        );
    }

    /**
     * Export couts des services payes par E-Cosplay.
     */
    #[Route('/export/couts-services', name: 'export_couts_services')]
    public function exportCoutsServices(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $format = $request->query->getString('format', 'csv');

        return $this->helper->exportResponse(
            $this->exportService->buildCoutsServicesData($from, $to),
            'couts_services_'.$from->format('Ymd').'_'.$to->format('Ymd'),
            $format,
        );
    }

    /**
     * Rapport financier public (PDF uniquement).
     * Resume : recettes par service, depenses (commissions, infra), bilan.
     * Aucune donnee nominative client.
     */
    #[Route('/rapport-financier', name: 'rapport_financier')]
    public function rapportFinancier(Request $request): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $periodFrom = $from->format(ComptaExportService::DATE_FORMAT_FR);
        $periodTo = $to->format(ComptaExportService::DATE_FORMAT_FR);

        $factures = $this->em->createQueryBuilder()
            ->select('f')
            ->from(Facture::class, 'f')
            ->where('f.createdAt BETWEEN :from AND :to')
            ->andWhere('f.isPaid = true')
            ->setParameter('from', $from)
            ->setParameter('to', $to)
            ->getQuery()
            ->getResult();

        $recettes = $this->computeRecettes($factures);
        $depenses = $this->computeDepenses($from, $to, $factures);

        $pdf = new RapportFinancierPdf($this->kernel, $periodFrom, $periodTo);
        $pdf->setData($recettes, $depenses);
        $pdf->generate();

        $filename = 'rapport_financier_'.$from->format('Ymd').'_'.$to->format('Ymd').'.pdf';

        return new Response($pdf->Output('S'), 200, [
            'Content-Type' => 'application/pdf',
            'Content-Disposition' => self::CONTENT_DISPOSITION_PREFIX.$filename.'"',
        ]);
    }

    /**
     * Rapport financier public + signature DocuSeal.
     */
    #[Route('/rapport-financier/sign', name: 'rapport_financier_sign')]
    public function rapportFinancierSign(Request $request, DocuSealService $docuSeal): Response
    {
        [$from, $to] = $this->helper->resolvePeriod($request);
        $periodFrom = $from->format(ComptaExportService::DATE_FORMAT_FR);
        $periodTo = $to->format(ComptaExportService::DATE_FORMAT_FR);

        // Generer le meme PDF que rapportFinancier
        $fakeRequest = new Request();
        $fakeRequest->query->set('period', 'custom');
        $fakeRequest->query->set('from', $from->format('Y-m-d'));
        $fakeRequest->query->set('to', $to->format('Y-m-d'));
        $response = $this->rapportFinancier($fakeRequest);
        $pdfContent = $response->getContent();

        /** @var \App\Entity\User $user */
        $user = $this->getUser();
        $redirectUrl = $this->generateUrl('app_admin_comptabilite_sign_callback', [
            'type' => 'rapport-financier',
            'period' => $request->query->getString('period', 'current'),
            'from' => $request->query->getString('from', ''),
            'to' => $request->query->getString('to', ''),
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $submitterId = $docuSeal->sendComptaForSignature(
            $pdfContent,
            'Rapport financier - '.$periodFrom.' au '.$periodTo,
            $user->getEmail(),
            $user->getFullName(),
            ['export_type' => 'rapport-financier', 'period_from' => $from->format('Y-m-d'), 'period_to' => $to->format('Y-m-d')],
            $redirectUrl,
        );

        if (null !== $submitterId) {
            $slug = $docuSeal->getSubmitterSlug($submitterId);
            if (null !== $slug) {
                $request->getSession()->set('compta_submitter_id', $submitterId);

                return $this->redirect(rtrim($this->docuSealUrl, '/').'/s/'.$slug);
            }
        }

        $this->addFlash('error', 'Erreur lors de la creation de la signature DocuSeal.');

        return $this->redirectToRoute('app_admin_comptabilite_index');
    }

    /**
     * Calcule les recettes par service a partir des factures.
     *
     * @param list<Facture> $factures
     *
     * @return array<string, float>
     */
    private function computeRecettes(array $factures): array
    {
        $serviceGroups = [
            'esite' => ['types' => ['website', 'hosting', 'maintenance'], 'name' => 'Services web (E-Site)'],
            'esymail' => ['types' => ['esymail'], 'name' => 'Messagerie (E-Mail)'],
            'ndd' => ['types' => ['ndd'], 'name' => 'Noms de domaine'],
            'other' => ['types' => ['other'], 'name' => 'Autres services numeriques'],
        ];

        $raw = [];
        foreach (ComptaExportService::SERVICE_COSTS as $type => $config) {
            $raw[$type] = ['ca_ht' => 0.0];
        }

        foreach ($factures as $facture) {
            foreach ($facture->getLines() as $line) {
                $type = $line->getType() ?? 'other';
                if (!isset($raw[$type])) {
                    $type = 'other';
                }
                $raw[$type]['ca_ht'] += (float) $line->getPriceHt();
            }
        }

        $recettes = [];
        foreach ($serviceGroups as $group) {
            $ca = 0.0;
            foreach ($group['types'] as $t) {
                $ca += $raw[$t]['ca_ht'];
            }
            if ($ca > 0) {
                $recettes[$group['name']] = $ca;
            }
        }

        return $recettes;
    }

    /**
     * Calcule les depenses (commissions Stripe, couts services, infra) sur la periode.
     *
     * @param list<Facture> $factures
     *
     * @return array<string, float>
     */
    /**
     * @param list<Facture> $factures
     *
     * @return array<string, float>
     */
    private function computeDepenses(\DateTimeImmutable $from, \DateTimeImmutable $to, array $factures): array
    {
        $totalCommission = $this->exportService->computeStripeCommissions($from, $to);
        $coutServices = $this->exportService->computeServiceCostsFromFactures($factures);

        $depenses = [];
        if ($totalCommission > 0) {
            $depenses['Commissions Stripe (1,5% + 0,25 E/transaction)'] = $totalCommission;
        }
        if ($coutServices > 0) {
            $depenses['Prestataires services numeriques'] = $coutServices;
        }
        $depenses['Infrastructure serveur'] = 80.0;

        return $depenses;
    }

    /**
     * Telecharge les documents signes depuis DocuSeal (PDF signe + certificat d'audit).
     *
     * @param array<string, mixed> $submitterData
     *
     * @return list<array{path: string, name: string}>
     */
    private function downloadSignedDocuments(array $submitterData, string $type): array
    {
        $documents = $submitterData['documents'] ?? [];
        $pdfUrl = $documents[0]['url'] ?? null;
        $auditUrl = $submitterData['audit_log_url'] ?? null;
        $attachments = [];

        if (null !== $pdfUrl) {
            $content = @file_get_contents($pdfUrl);
            if (false !== $content && str_starts_with($content, '%PDF')) {
                $tmpSigned = tempnam(sys_get_temp_dir(), 'compta_signed_').'.pdf';
                file_put_contents($tmpSigned, $content);
                $attachments[] = ['path' => $tmpSigned, 'name' => $type.'_signe.pdf'];
            }
        }

        if (null !== $auditUrl) {
            $auditContent = @file_get_contents($auditUrl);
            if (false !== $auditContent) {
                $tmpAudit = tempnam(sys_get_temp_dir(), 'compta_audit_').'.pdf';
                file_put_contents($tmpAudit, $auditContent);
                $attachments[] = ['path' => $tmpAudit, 'name' => $type.'_certificat_audit.pdf'];
            }
        }

        return $attachments;
    }

    /**
     * Envoie le document signe par email et nettoie les fichiers temporaires.
     *
     * @param array<string, mixed>                     $submitterData
     * @param list<array{path: string, name: string}> $attachments
     */
    private function sendSignedDocumentEmail(array $submitterData, string $type, object $user, array $attachments, MailerService $mailer, \Twig\Environment $twig): void
    {
        $titleMap = [
            'journal-ventes' => ComptaExportService::LABEL_JOURNAL_VENTES,
            'grand-livre' => ComptaExportService::LABEL_GRAND_LIVRE,
            'fec' => 'FEC',
            'balance-agee' => 'Balance agee',
            'reglements' => 'Reglements',
            'commissions-stripe' => ComptaExportService::LABEL_COMMISSIONS_STRIPE,
            'couts-services' => 'Couts services',
            'rapport-financier' => 'Rapport financier',
        ];
        $title = $titleMap[$type] ?? $type;

        $metadata = $submitterData['metadata'] ?? [];
        $periodFrom = isset($metadata['period_from'])
            ? (new \DateTimeImmutable($metadata['period_from']))->format(ComptaExportService::DATE_FORMAT_FR)
            : '';
        $periodTo = isset($metadata['period_to'])
            ? (new \DateTimeImmutable($metadata['period_to']))->format(ComptaExportService::DATE_FORMAT_FR)
            : '';

        $htmlContent = $twig->render('emails/compta_export_signed.html.twig', [
            'userName' => $user->getFullName(),
            'documentTitle' => $title,
            'periodFrom' => $periodFrom,
            'periodTo' => $periodTo,
            'signedAt' => (new \DateTimeImmutable())->format(ComptaExportService::DATE_FORMAT_FR.' H:i'),
        ]);

        $mailer->sendEmail(
            $user->getEmail(),
            'Export comptable signe - '.$title,
            $htmlContent,
            null,
            null,
            false,
            $attachments,
        );

        foreach ($attachments as $att) {
            @unlink($att['path']);
        }
    }

    /**
     * Retourne la map de titres pour les exports PDF comptables.
     *
     * @return array<string, string>
     */
    private function getExportTitleMap(): array
    {
        return [
            'journal-ventes' => ComptaExportService::LABEL_JOURNAL_VENTES,
            'grand-livre' => ComptaExportService::LABEL_GRAND_LIVRE,
            'fec' => 'Fichier des Ecritures Comptables (FEC)',
            'balance-agee' => 'Balance agee - Creances clients',
            'reglements' => 'Liste des reglements',
            'commissions-stripe' => ComptaExportService::LABEL_COMMISSIONS_STRIPE,
            'couts-services' => 'Couts services E-Cosplay',
            'echeanciers' => 'Echeanciers de paiement',
        ];
    }
}
