<?php

namespace App\Service;

use App\Entity\ActionLog;
use App\Entity\Customer;
use App\Entity\Domain;
use App\Entity\DomainEmail;
use App\Entity\Website;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;


/**
 * Service centralise pour les actions destructrices et critiques.
 * Chaque action est loggee de facon exhaustive (ActionLog + Psr\Log).
 */
class ActionService
{
    private const REASON_PAYMENT_RECEIVED = 'Paiement recu';

    public function __construct(
        private EntityManagerInterface $em,
        private LoggerInterface $logger,
    ) {
    }

    // ──────── Suspension client ────────

    /**
     * Suspend un client : desactive le compte + suspend tous ses sites + suspend tous ses emails.
     */
    public function suspendCustomer(Customer $customer, string $reason = 'Impaye'): bool
    {
        $previousState = $customer->getState();

        if (Customer::STATE_SUSPENDED === $previousState) {
            $this->log(ActionLog::ACTION_SUSPEND_CUSTOMER, $customer, 'Client deja suspendu', 'warning', true);

            return true;
        }

        $this->log(ActionLog::ACTION_SUSPEND_CUSTOMER, $customer,
            'Debut suspension client '.$customer->getFullName().' ('.$customer->getEmail().') - Raison: '.$reason,
            'critical', true, ['previous' => $previousState, 'new' => Customer::STATE_SUSPENDED]
        );

        try {
            // 1. Suspend le compte client
            $customer->setState(Customer::STATE_SUSPENDED);
            $this->em->flush();

            $this->log(ActionLog::ACTION_SUSPEND_CUSTOMER, $customer,
                'Compte client passe en suspended', 'info', true, ['previous' => $previousState, 'new' => Customer::STATE_SUSPENDED]
            );

            // 2. Suspend tous les sites
            $websites = $this->em->getRepository(Website::class)->findBy(['customer' => $customer]);
            foreach ($websites as $website) {
                $this->suspendWebsite($website, $customer, $reason);
            }

            // 3. Suspend tous les emails
            $domains = $this->em->getRepository(Domain::class)->findBy(['customer' => $customer]);
            foreach ($domains as $domain) {
                $emails = $this->em->getRepository(DomainEmail::class)->findBy(['domain' => $domain]);
                foreach ($emails as $email) {
                    $this->suspendDomainEmail($email, $customer, $reason);
                }
            }

            $this->log(ActionLog::ACTION_SUSPEND_CUSTOMER, $customer,
                'Suspension complete - Sites: '.\count($websites).' - Domaines: '.\count($domains),
                'critical', true
            );

            return true;
        } catch (\Throwable $e) {
            $this->logError(ActionLog::ACTION_SUSPEND_CUSTOMER, $customer, 'Erreur suspension client', $e);

            return false;
        }
    }

    /**
     * Retablit un client suspendu : reactive le compte + reactive les sites + reactive les emails.
     */
    public function unsuspendCustomer(Customer $customer, string $reason = self::REASON_PAYMENT_RECEIVED): bool
    {
        $previousState = $customer->getState();

        if (Customer::STATE_SUSPENDED !== $previousState) {
            $this->log(ActionLog::ACTION_UNSUSPEND_CUSTOMER, $customer, 'Client non suspendu, rien a faire', 'warning', true);

            return true;
        }

        $this->log(ActionLog::ACTION_UNSUSPEND_CUSTOMER, $customer,
            'Debut retablissement client '.$customer->getFullName().' - Raison: '.$reason,
            'info', true, ['previous' => $previousState, 'new' => Customer::STATE_ACTIVE]
        );

        try {
            $customer->setState(Customer::STATE_ACTIVE);
            $this->em->flush();

            // Reactive les sites
            $websites = $this->em->getRepository(Website::class)->findBy(['customer' => $customer, 'state' => Website::STATE_SUSPENDED]);
            foreach ($websites as $website) {
                $this->unsuspendWebsite($website, $customer, $reason);
            }

            // Reactive les emails
            $domains = $this->em->getRepository(Domain::class)->findBy(['customer' => $customer]);
            foreach ($domains as $domain) {
                $emails = $this->em->getRepository(DomainEmail::class)->findBy(['domain' => $domain, 'state' => 'suspended']);
                foreach ($emails as $email) {
                    $this->unsuspendDomainEmail($email, $customer, $reason);
                }
            }

            $this->log(ActionLog::ACTION_UNSUSPEND_CUSTOMER, $customer,
                'Retablissement complet', 'info', true
            );

            return true;
        } catch (\Throwable $e) {
            $this->logError(ActionLog::ACTION_UNSUSPEND_CUSTOMER, $customer, 'Erreur retablissement client', $e);

            return false;
        }
    }

    // ──────── Suspension site ────────

    public function suspendWebsite(Website $website, Customer $customer, string $reason = 'Impaye'): void
    {
        $prev = $website->getState();
        if (Website::STATE_SUSPENDED === $prev) {
            return;
        }

        $website->setState(Website::STATE_SUSPENDED);
        $this->em->flush();

        $this->log(ActionLog::ACTION_SUSPEND_WEBSITE, $customer,
            'Site '.$website->getName().' (UUID '.$website->getUuid().') suspendu - Raison: '.$reason,
            'critical', true, ['previous' => $prev, 'new' => Website::STATE_SUSPENDED, 'entityId' => $website->getId(), 'entityType' => 'Website']
        );
    }

    public function unsuspendWebsite(Website $website, Customer $customer, string $reason = self::REASON_PAYMENT_RECEIVED): void
    {
        $prev = $website->getState();
        $website->setState(Website::STATE_OPEN);
        $this->em->flush();

        $this->log(ActionLog::ACTION_UNSUSPEND_WEBSITE, $customer,
            'Site '.$website->getName().' retabli - Raison: '.$reason,
            'info', true, ['previous' => $prev, 'new' => Website::STATE_OPEN, 'entityId' => $website->getId(), 'entityType' => 'Website']
        );
    }

    // ──────── Suspension email ────────

    public function suspendDomainEmail(DomainEmail $email, Customer $customer, string $reason = 'Impaye'): void
    {
        $prev = $email->getState();
        if ('suspended' === $prev) {
            return;
        }

        $email->setState('suspended');
        $this->em->flush();

        $this->log(ActionLog::ACTION_SUSPEND_DOMAIN_EMAIL, $customer,
            'Email '.$email->getFullEmail().' suspendu - Raison: '.$reason,
            'critical', true, ['previous' => $prev, 'new' => 'suspended', 'entityId' => $email->getId(), 'entityType' => 'DomainEmail']
        );
    }

    public function unsuspendDomainEmail(DomainEmail $email, Customer $customer, string $reason = self::REASON_PAYMENT_RECEIVED): void
    {
        $prev = $email->getState();
        $email->setState('active');
        $this->em->flush();

        $this->log(ActionLog::ACTION_UNSUSPEND_DOMAIN_EMAIL, $customer,
            'Email '.$email->getFullEmail().' retabli - Raison: '.$reason,
            'info', true, ['previous' => $prev, 'new' => 'active', 'entityId' => $email->getId(), 'entityType' => 'DomainEmail']
        );
    }

    // ──────── Desactivation + suppression donnees ────────

    /**
     * Desactive definitivement un client (pre-suppression).
     */
    public function disableCustomer(Customer $customer, string $reason = 'Resiliation pour impaye'): bool
    {
        $previousState = $customer->getState();

        $this->log(ActionLog::ACTION_DISABLE_CUSTOMER, $customer,
            'Desactivation definitive '.$customer->getFullName().' - Raison: '.$reason,
            'critical', true, ['previous' => $previousState, 'new' => Customer::STATE_DISABLED]
        );

        try {
            $customer->setState(Customer::STATE_DISABLED);
            $this->em->flush();

            return true;
        } catch (\Throwable $e) {
            $this->logError(ActionLog::ACTION_DISABLE_CUSTOMER, $customer, 'Erreur desactivation', $e);

            return false;
        }
    }

    /**
     * Marque un client pour suppression (pending_delete, sera traite par le cron CleanPendingDeleteCommand).
     */
    public function markForDeletion(Customer $customer, string $reason = 'Resiliation contrat + recouvrement'): bool
    {
        $previousState = $customer->getState();

        $this->log(ActionLog::ACTION_DELETE_CUSTOMER_DATA, $customer,
            'Marquage suppression client '.$customer->getFullName().' ('.$customer->getEmail().') - Raison: '.$reason.' - Toutes les donnees seront supprimees par le cron nocturne.',
            'critical', true, ['previous' => $previousState, 'new' => Customer::STATE_PENDING_DELETE]
        );

        try {
            $customer->setState(Customer::STATE_PENDING_DELETE);
            $this->em->flush();

            $this->logger->critical('ActionService: client '.$customer->getEmail().' marque pour suppression - '.$reason);

            return true;
        } catch (\Throwable $e) {
            $this->logError(ActionLog::ACTION_DELETE_CUSTOMER_DATA, $customer, 'Erreur marquage suppression', $e);

            return false;
        }
    }

    // ──────── Logging ────────

    /**
     * @param array{previous?: ?string, new?: ?string, entityId?: ?int, entityType?: ?string} $extra
     */
    private function log(
        string $action,
        Customer $customer,
        string $message,
        string $severity = 'info',
        bool $success = true,
        array $extra = [],
    ): void {
        $previousState = $extra['previous'] ?? null;
        $newState = $extra['new'] ?? null;
        $entityId = $extra['entityId'] ?? null;
        $entityType = $extra['entityType'] ?? null;

        $log = new ActionLog($action, $message, $severity, $success);
        $log->setCustomer($customer);
        $log->setPreviousState($previousState);
        $log->setNewState($newState);

        if (null !== $entityId) {
            $log->setEntityId($entityId);
            $log->setEntityType($entityType);
        }

        $this->em->persist($log);
        $this->em->flush();

        $logMethod = match ($severity) {
            'critical' => 'critical',
            'danger' => 'error',
            'warning' => 'warning',
            default => 'info',
        };

        $this->logger->$logMethod('ActionService: '.$message, [
            'action' => $action,
            'customer_id' => $customer->getId(),
            'customer_email' => $customer->getEmail(),
            'previous_state' => $previousState,
            'new_state' => $newState,
            'entity_id' => $entityId,
            'entity_type' => $entityType,
        ]);
    }

    private function logError(string $action, Customer $customer, string $message, \Throwable $e): void
    {
        $log = new ActionLog($action, $message, 'critical', false);
        $log->setCustomer($customer);
        $log->setErrorMessage($e->getMessage());
        $log->setContext(json_encode([
            'exception' => $e::class,
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => array_slice(explode("\n", $e->getTraceAsString()), 0, 5),
        ]));

        $this->em->persist($log);
        $this->em->flush();

        $this->logger->critical('ActionService ERROR: '.$message.': '.$e->getMessage(), [
            'action' => $action,
            'customer_id' => $customer->getId(),
            'exception' => $e,
        ]);
    }
}
