<?php

namespace App\Services;

use DateTime;

/**
 * Servicio de Firmas
 * Gestión de firmas digitales de contratos
 */
class SignatureService
{
    private Database $db;
    private Logger $logger;
    private SecurityService $security;
    private string $storagePath;

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->logger = new Logger();
        $this->security = new SecurityService();
        $this->storagePath = storage_path();
    }

    // ========================================================================
    // PROCESO DE FIRMA
    // ========================================================================

    /**
     * Registrar firma de contrato
     */
    public function sign(string $token, array $signatureData): array
    {
        // Validar datos requeridos
        $required = [
            'firma_imagen',
            'selfie',
            'documento_frontal',
            'documento_trasero',
            'tipo_documento',
            'numero_documento'
        ];

        foreach ($required as $field) {
            if (empty($signatureData[$field])) {
                $this->recordFailedAttempt($token, 'missing_field', $field);
                return [
                    'success' => false,
                    'message' => "El campo {$field} es requerido."
                ];
            }
        }

        // Obtener contrato
        $contractService = new ContractService();
        $canSign = $contractService->canBeSigned($token);

        if (!$canSign['can_sign']) {
            $this->recordFailedAttempt($token, 'cannot_sign', $canSign['message']);
            return [
                'success' => false,
                'message' => $canSign['message']
            ];
        }

        $contract = $canSign['contract'];

        // Validar que no haya sido firmado previamente
        if ($this->hasBeenSigned($contract['id'])) {
            $this->recordFailedAttempt($token, 'already_signed', 'Contract already signed');
            return [
                'success' => false,
                'message' => 'Este contrato ya ha sido firmado.'
            ];
        }

        // Verificar rate limiting (máximo 5 intentos por IP)
        if (!$this->checkSignatureAttempts($contract['id'])) {
            return [
                'success' => false,
                'message' => 'Demasiados intentos fallidos. Contacte al administrador.'
            ];
        }

        // Validar imágenes
        $validationResult = $this->validateImages($signatureData);
        if (!$validationResult['valid']) {
            $this->recordFailedAttempt($token, 'invalid_images', $validationResult['message']);
            return [
                'success' => false,
                'message' => $validationResult['message']
            ];
        }

        // Validar documento
        if (!$this->security->validateDocumentNumber(
            $signatureData['numero_documento'],
            $signatureData['tipo_documento']
        )) {
            $this->recordFailedAttempt($token, 'invalid_document', 'Invalid document number');
            return [
                'success' => false,
                'message' => 'El número de documento no es válido.'
            ];
        }

        try {
            $this->db->beginTransaction();

            // Guardar imágenes
            $paths = $this->saveImages($contract['id'], $signatureData);

            // Preparar metadata
            $metadata = [
                'screen_width'    => $signatureData['screen_width']    ?? null,
                'screen_height'   => $signatureData['screen_height']   ?? null,
                'timezone'        => $signatureData['timezone']        ?? null,
                'language'        => $signatureData['language']        ?? null,
                'nombre_completo' => $signatureData['nombre_completo'] ?? null,
                'fecha_nacimiento'=> $signatureData['fecha_nacimiento'] ?? null,
                'fecha_expedicion'=> $signatureData['fecha_expedicion'] ?? null,
                'telefono'        => $signatureData['telefono']        ?? null,
            ];

            // Insertar firma
            $signatureId = $this->db->insert('signatures', [
                'contract_id'            => $contract['id'],
                'firma_imagen_path'      => $paths['firma'],
                'selfie_path'            => $paths['selfie'],
                'documento_frontal_path' => $paths['documento_frontal'],
                'documento_trasero_path' => $paths['documento_trasero'],
                'tipo_documento'         => $signatureData['tipo_documento'],
                'numero_documento'       => $signatureData['numero_documento'],
                'nombre_completo'        => $signatureData['nombre_completo']  ?? null,
                'fecha_nacimiento'       => $signatureData['fecha_nacimiento'] ?? null,
                'fecha_expedicion'       => $signatureData['fecha_expedicion'] ?? null,
                'telefono'               => $signatureData['telefono']         ?? null,
                'ip_address'             => $this->security->getClientIP(),
                'user_agent'             => $this->security->getUserAgent(),
                'ubicacion_lat'          => $signatureData['lat']       ?? null,
                'ubicacion_lng'          => $signatureData['lng']       ?? null,
                'ubicacion_precision'    => $signatureData['precision'] ?? null,
                'metadata'               => json_encode($metadata)
            ]);

            // Actualizar estado del contrato
            $contractService->updateStatus($contract['id'], 'firmado');

            // Registrar intento exitoso
            $this->recordAttempt($contract['id'], true);

            $this->db->commit();

            $this->logger->logSignature(
                $contract['id'],
                'signature_completed',
                "Contrato firmado por: {$contract['cliente_nombre']}",
                [
                    'signature_id' => $signatureId,
                    'documento' => $signatureData['numero_documento']
                ]
            );

            return [
                'success' => true,
                'message' => 'Contrato firmado exitosamente.',
                'signature_id' => $signatureId,
                'contract' => $contract
            ];
        } catch (\Exception $e) {
            $this->db->rollback();
            $this->logger->error('Error signing contract: ' . $e->getMessage());
            $this->recordFailedAttempt($token, 'system_error', $e->getMessage());

            return [
                'success' => false,
                'message' => 'Error al procesar la firma. Intente nuevamente.'
            ];
        }
    }

    /**
     * Validar todas las imágenes
     */
    private function validateImages(array $data): array
    {
        // Validar firma
        if (!$this->security->validateSignature($data['firma_imagen'])) {
            return [
                'valid' => false,
                'message' => 'La firma no es válida o está vacía.'
            ];
        }

        // Validar selfie
        if (!$this->security->validateImage($data['selfie'])) {
            return [
                'valid' => false,
                'message' => 'La selfie no es válida.'
            ];
        }

        // Validar documento frontal
        if (!$this->security->validateImage($data['documento_frontal'])) {
            return [
                'valid' => false,
                'message' => 'La foto del documento frontal no es válida.'
            ];
        }

        // Validar documento trasero
        if (!$this->security->validateImage($data['documento_trasero'])) {
            return [
                'valid' => false,
                'message' => 'La foto del documento trasero no es válida.'
            ];
        }

        return ['valid' => true];
    }

    /**
     * Guardar imágenes en storage
     */
    private function saveImages(int $contractId, array $data): array
    {
        $timestamp = time();
        $paths = [];

        // Guardar firma
        $paths['firma'] = $this->saveBase64Image(
            $data['firma_imagen'],
            "signatures/contract_{$contractId}_firma_{$timestamp}.png"
        );

        // Guardar selfie
        $paths['selfie'] = $this->saveBase64Image(
            $data['selfie'],
            "photos/contract_{$contractId}_selfie_{$timestamp}.jpg"
        );

        // Guardar documento frontal
        $paths['documento_frontal'] = $this->saveBase64Image(
            $data['documento_frontal'],
            "documents/contract_{$contractId}_doc_frontal_{$timestamp}.jpg"
        );

        // Guardar documento trasero
        $paths['documento_trasero'] = $this->saveBase64Image(
            $data['documento_trasero'],
            "documents/contract_{$contractId}_doc_trasero_{$timestamp}.jpg"
        );

        return $paths;
    }

    /**
     * Guardar imagen base64
     */
    private function saveBase64Image(string $base64Data, string $relativePath): string
    {
        // Remover header de base64
        $imageData = preg_replace('/^data:image\/\w+;base64,/', '', $base64Data);
        $decoded = base64_decode($imageData);

        $fullPath = $this->storagePath . '/' . $relativePath;

        // Crear directorio si no existe
        $dir = dirname($fullPath);
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        // Guardar archivo
        file_put_contents($fullPath, $decoded);

        return $relativePath;
    }

    /**
     * Verificar si un contrato ha sido firmado
     */
    private function hasBeenSigned(int $contractId): bool
    {
        return $this->db->exists(
            'signatures',
            'contract_id = :contract_id',
            [':contract_id' => $contractId]
        );
    }

    /**
     * Obtener firma por contrato
     */
    public function getByContract(int $contractId): ?array
    {
        return $this->db->fetchOne(
            "SELECT * FROM signatures WHERE contract_id = :contract_id",
            [':contract_id' => $contractId]
        );
    }

    /**
     * Obtener imagen de firma (para descargar)
     */
    public function getImage(int $signatureId, string $type): ?string
    {
        $signature = $this->db->fetchOne(
            "SELECT * FROM signatures WHERE id = :id",
            [':id' => $signatureId]
        );

        if (!$signature) {
            return null;
        }

        $pathField = match($type) {
            'firma' => 'firma_imagen_path',
            'selfie' => 'selfie_path',
            'documento_frontal' => 'documento_frontal_path',
            'documento_trasero' => 'documento_trasero_path',
            default => null
        };

        if (!$pathField || empty($signature[$pathField])) {
            return null;
        }

        $fullPath = $this->storagePath . '/' . $signature[$pathField];

        if (!file_exists($fullPath)) {
            return null;
        }

        return $fullPath;
    }

    // ========================================================================
    // CONTROL DE INTENTOS
    // ========================================================================

    /**
     * Verificar intentos de firma
     */
    private function checkSignatureAttempts(int $contractId): bool
    {
        $ip = $this->security->getClientIP();
        $maxAttempts = (int) ($_ENV['RATE_LIMIT_MAX_ATTEMPTS'] ?? 5);

        // Contar intentos fallidos en última hora
        $count = $this->db->count(
            'signature_attempts',
            'contract_id = :contract_id AND ip_address = :ip AND exitoso = 0 AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)',
            [':contract_id' => $contractId, ':ip' => $ip]
        );

        return $count < $maxAttempts;
    }

    /**
     * Registrar intento de firma
     */
    private function recordAttempt(int $contractId, bool $exitoso = true, string $razon = null, string $paso = null): void
    {
        $this->db->insert('signature_attempts', [
            'contract_id' => $contractId,
            'ip_address' => $this->security->getClientIP(),
            'user_agent' => $this->security->getUserAgent(),
            'exitoso' => $exitoso ? 1 : 0,
            'razon_fallo' => $razon,
            'paso_fallido' => $paso
        ]);
    }

    /**
     * Registrar intento fallido
     */
    private function recordFailedAttempt(string $token, string $razon, string $paso = null): void
    {
        $contractService = new ContractService();
        $contract = $contractService->getByToken($token);

        if ($contract) {
            $this->recordAttempt($contract['id'], false, $razon, $paso);

            $this->logger->logSignature(
                $contract['id'],
                'signature_attempt_failed',
                "Failed: {$razon}",
                ['paso' => $paso, 'ip' => $this->security->getClientIP()]
            );
        }
    }

    /**
     * Obtener estadísticas de intentos
     */
    public function getAttemptStats(int $contractId): array
    {
        return $this->db->fetchOne(
            "SELECT
                COUNT(*) as total_intentos,
                SUM(exitoso) as intentos_exitosos,
                SUM(CASE WHEN exitoso = 0 THEN 1 ELSE 0 END) as intentos_fallidos
             FROM signature_attempts
             WHERE contract_id = :contract_id",
            [':contract_id' => $contractId]
        ) ?: [
            'total_intentos' => 0,
            'intentos_exitosos' => 0,
            'intentos_fallidos' => 0
        ];
    }
}
