<?php

declare(strict_types=1);

namespace App\Services;

require_once __DIR__ . '/LogService.php';
require_once __DIR__ . '/ApiService.php';
require_once __DIR__ . '/FileService.php';
require_once __DIR__ . '/ReceiptPDFGenerator.php';
require_once __DIR__ . '/EmailTemplates.php';

use PDO;
use Database;
use App\Services\LogService;
use App\Services\FileService;
use App\Services\ApiService;
use Exception;
use DateTime;

/**
 * Service de gestion des reçus pour les passages de type don (fk_type=1)
 * Optimisé pour générer des PDF très légers (< 20KB)
 */
class ReceiptService {
    private PDO $db;
    private FileService $fileService;
    private const DEFAULT_LOGO_PATH = __DIR__ . '/../../docs/_logo_recu.png';
    private const LOGO_WIDTH = 40;  // Largeur du logo en mm (80 est trop grand pour un A4)
    private const LOGO_HEIGHT = 40; // Hauteur du logo en mm
    
    public function __construct() {
        $this->db = Database::getInstance();
        $this->fileService = new FileService();
    }
    
    /**
     * Génère un reçu pour un passage de type don avec email valide
     * 
     * @param int $passageId ID du passage
     * @return bool True si le reçu a été généré avec succès
     */
    public function generateReceiptForPassage(int $passageId): bool {
        try {
            // Récupérer les données du passage
            $passageData = $this->getPassageData($passageId);
            if (!$passageData) {
                LogService::log('Passage non trouvé pour génération de reçu', [
                    'level' => 'warning',
                    'passageId' => $passageId
                ]);
                return false;
            }
            
            // Vérifier que c'est un don effectué (fk_type = 1) ou un lot (fk_type = 5) avec email valide
            $fkType = (int)$passageData['fk_type'];
            if ($fkType !== 1 && $fkType !== 5) {
                return false; // Ni don ni lot, pas de reçu
            }
            
            // Déchiffrer et vérifier l'email
            $email = '';
            if (!empty($passageData['encrypted_email'])) {
                $email = ApiService::decryptSearchableData($passageData['encrypted_email']);
            }
            
            if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
                LogService::log('Email invalide ou manquant pour le reçu', [
                    'level' => 'info',
                    'passageId' => $passageId
                ]);
                return false;
            }
            
            // Récupérer les données de l'opération
            $operationData = $this->getOperationData($passageData['fk_operation']);
            if (!$operationData) {
                return false;
            }
            
            // Récupérer les données de l'entité
            $entiteData = $this->getEntiteData($operationData['fk_entite']);
            if (!$entiteData) {
                return false;
            }
            
            // Récupérer le logo de l'entité
            $logoPath = $this->getEntiteLogo($operationData['fk_entite']);
            
            // Préparer les données pour la génération du PDF
            $receiptData = $this->prepareReceiptData($passageData, $operationData, $entiteData, $email);

            // Créer le répertoire de stockage
            $uploadPath = "/{$operationData['fk_entite']}/recus/{$operationData['id']}";
            $fullPath = $this->fileService->createDirectory($operationData['fk_entite'], $uploadPath);

            // Nom du fichier
            $fileName = 'recu_' . $passageId . '.pdf';
            $filePath = $fullPath . '/' . $fileName;

            // Générer le PDF directement dans le fichier
            $pdfGenerated = $this->generateOptimizedPDF($receiptData, $filePath, $logoPath);

            if (!$pdfGenerated || !file_exists($filePath)) {
                throw new Exception('Impossible de générer le fichier PDF');
            }

            // Appliquer les permissions
            $this->fileService->setFilePermissions($filePath);

            // Récupérer la taille du fichier généré
            $fileSize = filesize($filePath);

            // Enregistrer dans la table medias
            $mediaId = $this->saveToMedias(
                $operationData['fk_entite'],
                $operationData['id'],
                $passageId,
                $fileName,
                $filePath,
                $fileSize
            );

            // Mettre à jour le passage avec les infos du reçu
            $this->updatePassageReceipt($passageId, $fileName);

            // Ajouter à la queue d'email (le PDF sera lu depuis le fichier)
            $this->queueReceiptEmail($passageId, $email, $receiptData, $filePath);
            
            LogService::log('Reçu généré avec succès', [
                'level' => 'info',
                'passageId' => $passageId,
                'mediaId' => $mediaId,
                'fileName' => $fileName,
                'fileSize' => $fileSize
            ]);
            
            return true;
            
        } catch (Exception $e) {
            LogService::log('Erreur lors de la génération du reçu', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'passageId' => $passageId
            ]);
            return false;
        }
    }
    
    /**
     * Génère un PDF optimisé avec logo et mise en page épurée
     *
     * @param array $data Données du reçu
     * @param string $outputPath Chemin du fichier PDF à créer
     * @param string|null $logoPath Chemin du logo
     * @return bool True si la génération a réussi
     */
    private function generateOptimizedPDF(array $data, string $outputPath, ?string $logoPath): bool {
        $pdf = new ReceiptPDFGenerator();
        return $pdf->generateReceipt($data, $outputPath, $logoPath);
    }
    
    
    /**
     * Récupère les données du passage
     */
    private function getPassageData(int $passageId): ?array {
        $stmt = $this->db->prepare('
            SELECT p.*,
                   u.encrypted_name as user_encrypted_name,
                   u.encrypted_email as user_encrypted_email,
                   u.encrypted_phone as user_encrypted_phone
            FROM ope_pass p
            LEFT JOIN ope_users ou ON p.fk_user = ou.id
            LEFT JOIN users u ON ou.fk_user = u.id
            WHERE p.id = ? AND p.chk_active = 1
        ');
        $stmt->execute([$passageId]);
        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }
    
    /**
     * Récupère les données de l'opération
     */
    private function getOperationData(int $operationId): ?array {
        $stmt = $this->db->prepare('
            SELECT * FROM operations 
            WHERE id = ? AND chk_active = 1
        ');
        $stmt->execute([$operationId]);
        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }
    
    /**
     * Récupère les données de l'entité
     */
    private function getEntiteData(int $entiteId): ?array {
        $stmt = $this->db->prepare('
            SELECT * FROM entites 
            WHERE id = ? AND chk_active = 1
        ');
        $stmt->execute([$entiteId]);
        $entite = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($entite) {
            // Déchiffrer les données
            if (!empty($entite['encrypted_name'])) {
                $entite['name'] = ApiService::decryptData($entite['encrypted_name']);
            }
            if (!empty($entite['encrypted_email'])) {
                $entite['email'] = ApiService::decryptSearchableData($entite['encrypted_email']);
            }
            if (!empty($entite['encrypted_phone'])) {
                $entite['phone'] = ApiService::decryptData($entite['encrypted_phone']);
            }
        }
        
        return $entite ?: null;
    }
    
    /**
     * Récupère le chemin du logo de l'entité
     */
    private function getEntiteLogo(int $entiteId): ?string {
        $stmt = $this->db->prepare('
            SELECT file_path FROM medias 
            WHERE support = ? AND support_id = ? AND file_category = ?
            ORDER BY created_at DESC
            LIMIT 1
        ');
        $stmt->execute(['entite', $entiteId, 'logo']);
        $logo = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($logo && !empty($logo['file_path']) && file_exists($logo['file_path'])) {
            return $logo['file_path'];
        }
        
        // Utiliser le logo par défaut si disponible
        if (file_exists(self::DEFAULT_LOGO_PATH)) {
            return self::DEFAULT_LOGO_PATH;
        }
        
        return null;
    }
    
    /**
     * Prépare les données pour le reçu
     */
    private function prepareReceiptData(array $passage, array $operation, array $entite, string $email): array {
        // Déchiffrer le nom du donateur
        $donorName = '';
        if (!empty($passage['encrypted_name'])) {
            $donorName = ApiService::decryptData($passage['encrypted_name']);
        } elseif (!empty($passage['user_encrypted_name'])) {
            $donorName = ApiService::decryptData($passage['user_encrypted_name']);
        }
        
        // Construire l'adresse du donateur
        $donorAddress = [];
        if (!empty($passage['numero'])) $donorAddress[] = $passage['numero'];
        if (!empty($passage['rue'])) $donorAddress[] = $passage['rue'];
        if (!empty($passage['rue_bis'])) $donorAddress[] = $passage['rue_bis'];
        if (!empty($passage['ville'])) $donorAddress[] = $passage['ville'];
        
        // Date du don
        $donationDate = '';
        if (!empty($passage['passed_at'])) {
            $donationDate = date('d/m/Y', strtotime($passage['passed_at']));
        } elseif (!empty($passage['created_at'])) {
            $donationDate = date('d/m/Y', strtotime($passage['created_at']));
        }
        
        // Mode de règlement
        $paymentMethod = $this->getPaymentMethodLabel((int)($passage['fk_type_reglement'] ?? 1));
        
        // Adresse de l'entité
        $entiteAddress = [];
        if (!empty($entite['adresse1'])) $entiteAddress[] = $entite['adresse1'];
        if (!empty($entite['adresse2'])) $entiteAddress[] = $entite['adresse2'];
        if (!empty($entite['code_postal']) || !empty($entite['ville'])) {
            $entiteAddress[] = trim($entite['code_postal'] . ' ' . $entite['ville']);
        }
        
        return [
            'receipt_number' => $passage['id'],
            'entite_name' => $entite['name'] ?? 'Amicale des Sapeurs-Pompiers',
            'entite_address' => implode(' ', $entiteAddress),
            'entite_city' => $entite['ville'] ?? '',
            'entite_email' => $entite['email'] ?? '',
            'entite_phone' => $entite['phone'] ?? '',
            'donor_name' => $donorName,
            'donor_address' => implode(' ', $donorAddress),
            'donor_email' => $email,
            'donation_date' => $donationDate,
            'amount' => number_format((float)($passage['montant'] ?? 0), 2, ',', ' '),
            'payment_method' => $paymentMethod,
            'operation_name' => $operation['libelle'] ?? '',
            'signature_date' => date('d/m/Y')
        ];
    }
    
    /**
     * Retourne le libellé du mode de règlement
     */
    private function getPaymentMethodLabel(int $typeReglement): string {
        $stmt = $this->db->prepare('SELECT libelle FROM x_types_reglements WHERE id = ?');
        $stmt->execute([$typeReglement]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result ? $result['libelle'] : 'Espèces';
    }
    
    /**
     * Enregistre le fichier dans la table medias
     */
    private function saveToMedias(int $entiteId, int $operationId, int $passageId, string $fileName, string $filePath, int $fileSize): int {
        $stmt = $this->db->prepare('
            INSERT INTO medias (
                support, support_id, fichier, file_type, file_category,
                file_size, mime_type, original_name, fk_entite, fk_operation,
                file_path, description, created_at, fk_user_creat
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?)
        ');
        
        $stmt->execute([
            'passage',                    // support
            $passageId,                   // support_id
            $fileName,                    // fichier
            'pdf',                        // file_type
            'recu',                       // file_category
            $fileSize,                    // file_size
            'application/pdf',            // mime_type
            $fileName,                    // original_name
            $entiteId,                    // fk_entite
            $operationId,                 // fk_operation
            $filePath,                    // file_path
            'Reçu de don',               // description
            0                            // fk_user_creat (système)
        ]);
        
        return (int)$this->db->lastInsertId();
    }
    
    /**
     * Met à jour le passage avec les informations du reçu
     */
    private function updatePassageReceipt(int $passageId, string $fileName): void {
        $stmt = $this->db->prepare('
            UPDATE ope_pass 
            SET nom_recu = ?, date_creat_recu = NOW()
            WHERE id = ?
        ');
        $stmt->execute([$fileName, $passageId]);
    }
    
    /**
     * Ajoute le reçu à la queue d'email
     *
     * @param int $passageId ID du passage
     * @param string $email Email du destinataire
     * @param array $receiptData Données du reçu
     * @param string $pdfFilePath Chemin vers le fichier PDF
     */
    private function queueReceiptEmail(int $passageId, string $email, array $receiptData, string $pdfFilePath): void {
        // Lire le contenu du PDF depuis le fichier
        if (!file_exists($pdfFilePath)) {
            throw new \Exception('Fichier PDF introuvable pour la mise en queue: ' . $pdfFilePath);
        }

        $pdfContent = file_get_contents($pdfFilePath);
        if ($pdfContent === false) {
            throw new \Exception('Impossible de lire le fichier PDF: ' . $pdfFilePath);
        }

        // Préparer le sujet
        $subject = "Votre reçu de don - " . $receiptData['entite_name'];

        // Préparer les données pour le template
        $templateData = [
            'passage_id' => $passageId,
            'entite_name' => $receiptData['entite_name'],
            'donor_name' => $receiptData['donor_name'],
            'amount' => $receiptData['amount'],
            'donation_date' => $receiptData['donation_date'],
            'payment_method' => $receiptData['payment_method'],
            'entite_address' => $receiptData['entite_address'],
            'entite_email' => $receiptData['entite_email']
        ];

        // Générer le corps de l'email via le template centralisé
        $body = EmailTemplates::getReceiptDonationTemplate($templateData);

        // Préparer les headers avec pièce jointe
        $boundary = md5((string)time());
        $headers = "MIME-Version: 1.0\r\n";
        $headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n";

        // Corps complet avec pièce jointe
        $fullBody = "--$boundary\r\n";
        $fullBody .= "Content-Type: text/html; charset=UTF-8\r\n";
        $fullBody .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
        $fullBody .= $body . "\r\n\r\n";

        // Pièce jointe PDF
        $fullBody .= "--$boundary\r\n";
        $fullBody .= "Content-Type: application/pdf; name=\"recu_" . $receiptData['receipt_number'] . ".pdf\"\r\n";
        $fullBody .= "Content-Transfer-Encoding: base64\r\n";
        $fullBody .= "Content-Disposition: attachment; filename=\"recu_" . $receiptData['receipt_number'] . ".pdf\"\r\n\r\n";
        $fullBody .= chunk_split(base64_encode($pdfContent)) . "\r\n";
        $fullBody .= "--$boundary--";

        // Insérer dans la queue
        $stmt = $this->db->prepare('
            INSERT INTO email_queue (
                fk_pass, to_email, subject, body, headers, created_at, status
            ) VALUES (?, ?, ?, ?, ?, NOW(), ?)
        ');

        $stmt->execute([
            $passageId,
            $email,
            $subject,
            $fullBody,
            $headers,
            'pending'
        ]);
    }

    /**
     * Met à jour la date d'envoi du reçu
     */
    public function markReceiptAsSent(int $passageId): void {
        $stmt = $this->db->prepare('
            UPDATE ope_pass 
            SET date_sent_recu = NOW(), chk_email_sent = 1
            WHERE id = ?
        ');
        $stmt->execute([$passageId]);
    }
}