<?php

namespace App\Services;


/**
 * Servicio de Contratos
 * Gestión completa de contratos digitales
 */
class ContractService
{
    private Database $db;
    private Logger $logger;
    private SecurityService $security;

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

    // ========================================================================
    // CREACIÓN Y GESTIÓN
    // ========================================================================

    /**
     * Crear nuevo contrato
     */
    public function create(array $data): array
    {
        // Verificar si el email está habilitado
        $mailEnabled = filter_var($_ENV['MAIL_ENABLED'] ?? false, FILTER_VALIDATE_BOOLEAN);

        // Validar datos requeridos
        $required = ['admin_id', 'titulo', 'contenido', 'cliente_nombre'];

        // Solo requerir email si está habilitado
        if ($mailEnabled) {
            $required[] = 'cliente_email';
        }

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

        // Validar email solo si está presente y no está vacío
        if (!empty($data['cliente_email']) && !$this->security->validateEmail($data['cliente_email'])) {
            return [
                'success' => false,
                'message' => 'El email del cliente no es válido.'
            ];
        }

        // Sanitizar datos
        $titulo = $this->security->sanitizeString($data['titulo']);
        $contenido = $this->security->cleanHTML($data['contenido']);
        $clienteNombre = $this->security->sanitizeString($data['cliente_nombre']);
        $clienteEmail = !empty($data['cliente_email'])
            ? $this->security->sanitizeEmail($data['cliente_email'])
            : null;
        $clienteTelefono = isset($data['cliente_telefono'])
            ? $this->security->sanitizeString($data['cliente_telefono'])
            : null;
        $logoPath = !empty($data['logo_path'])
            ? $this->security->sanitizeString($data['logo_path'])
            : null;

        // Sanitizar datos de empresa (opcionales)
        $companyName = !empty($data['company_name'])
            ? $this->security->sanitizeString($data['company_name'])
            : null;
        $companyNit = !empty($data['company_nit'])
            ? $this->security->sanitizeString($data['company_nit'])
            : null;
        $companyAddress = !empty($data['company_address'])
            ? $this->security->sanitizeString($data['company_address'])
            : null;
        $companyPhone = !empty($data['company_phone'])
            ? $this->security->sanitizeString($data['company_phone'])
            : null;
        $companyEmail = !empty($data['company_email'])
            ? $this->security->sanitizeEmail($data['company_email'])
            : null;
        $companyWebsite = !empty($data['company_website'])
            ? $this->security->sanitizeString($data['company_website'])
            : null;
        $companyCity = !empty($data['company_city'])
            ? $this->security->sanitizeString($data['company_city'])
            : null;

        // Generar token único
        $token = $this->generateUniqueToken();

        // Calcular expiración
        $expirationHours = (int) ($data['expiration_hours'] ?? $_ENV['LINK_EXPIRATION_HOURS'] ?? 24);
        $expiraAt = gmdate('Y-m-d H:i:s', time() + ($expirationHours * 3600));

        try {
            // Insertar contrato
            $contractId = $this->db->insert('contracts', [
                'admin_id' => $data['admin_id'],
                'titulo' => $titulo,
                'contenido' => $contenido,
                'logo_path' => $logoPath,
                'token' => $token,
                'cliente_nombre' => $clienteNombre,
                'cliente_email' => $clienteEmail,
                'cliente_telefono' => $clienteTelefono,
                'estado' => 'pendiente',
                'expira_at' => $expiraAt,
                'company_name' => $companyName,
                'company_nit' => $companyNit,
                'company_address' => $companyAddress,
                'company_phone' => $companyPhone,
                'company_email' => $companyEmail,
                'company_website' => $companyWebsite,
                'company_city' => $companyCity
            ]);

            $this->logger->logAdminAccess(
                $data['admin_id'],
                'contract_created',
                "Contrato creado: {$titulo}",
                ['contract_id' => $contractId, 'cliente' => $clienteNombre]
            );

            return [
                'success' => true,
                'message' => 'Contrato creado exitosamente.',
                'contract_id' => $contractId,
                'token' => $token,
                'link' => $this->getSignatureLink($token)
            ];
        } catch (\Exception $e) {
            $this->logger->error('Error creating contract: ' . $e->getMessage());

            return [
                'success' => false,
                'message' => 'Error al crear el contrato: ' . $e->getMessage(),
                'error_detail' => [
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                    'trace' => $e->getTraceAsString()
                ]
            ];
        }
    }

    /**
     * Generar token único para contrato
     */
    private function generateUniqueToken(): string
    {
        do {
            $token = $this->security->generateToken(32);
        } while ($this->db->exists('contracts', 'token = :token', [':token' => $token]));

        return $token;
    }

    /**
     * Obtener link de firma
     */
    public function getSignatureLink(string $token): string
    {
        return base_url("contratos/firmar?token={$token}");
    }

    /**
     * Obtener contrato por ID
     */
    public function getById(int $id): ?array
    {
        return $this->db->fetchOne(
            "SELECT c.*, a.nombre as admin_nombre, a.email as admin_email
             FROM contracts c
             INNER JOIN admins a ON c.admin_id = a.id
             WHERE c.id = :id",
            [':id' => $id]
        );
    }

    /**
     * Obtener contrato por token
     */
    public function getByToken(string $token): ?array
    {
        return $this->db->fetchOne(
            "SELECT * FROM contracts WHERE token = :token",
            [':token' => $token]
        );
    }

    /**
     * Listar contratos — admin ve todos, asesor solo los suyos
     */
    public function listByAdmin(int $adminId, array $filters = [], string $rol = 'asesor'): array
    {
        if ($rol === 'administrador') {
            $where  = ['1=1'];
            $params = [];
        } else {
            $where  = ['c.admin_id = :admin_id'];
            $params = [':admin_id' => $adminId];
        }

        if (!empty($filters['estado'])) {
            $where[]           = 'c.estado = :estado';
            $params[':estado'] = $filters['estado'];
        }

        if (!empty($filters['search'])) {
            $where[]           = '(c.cliente_nombre LIKE :search OR c.cliente_email LIKE :search OR c.titulo LIKE :search OR s.nombre_completo LIKE :search OR s.numero_documento LIKE :search)';
            $params[':search'] = '%' . $filters['search'] . '%';
        }

        if (!empty($filters['fecha_desde'])) {
            $where[]                = 'DATE(c.created_at) >= :fecha_desde';
            $params[':fecha_desde'] = $filters['fecha_desde'];
        }

        if (!empty($filters['fecha_hasta'])) {
            $where[]                = 'DATE(c.created_at) <= :fecha_hasta';
            $params[':fecha_hasta'] = $filters['fecha_hasta'];
        }

        $whereSql = implode(' AND ', $where);
        $orderBy  = $filters['order_by'] ?? 'c.created_at DESC';
        $limit    = isset($filters['limit']) ? "LIMIT {$filters['limit']}" : '';

        $sql = "SELECT c.*,
                       s.nombre_completo  AS firmante_nombre,
                       s.telefono         AS firmante_telefono,
                       s.tipo_documento   AS firmante_tipo_doc,
                       s.numero_documento AS firmante_numero_doc,
                       s.firmado_at,
                       a.nombre           AS asesor_nombre
                FROM contracts c
                LEFT JOIN signatures s ON s.contract_id = c.id
                LEFT JOIN admins a ON a.id = c.admin_id
                WHERE {$whereSql}
                ORDER BY {$orderBy} {$limit}";

        return $this->db->fetchAll($sql, $params);
    }

    /**
     * Obtener estadísticas de contratos — admin ve todos, asesor solo los suyos
     */
    public function getStats(int $adminId, string $rol = 'asesor'): array
    {
        if ($rol === 'administrador') {
            $stats = $this->db->fetchOne(
                "SELECT
                    COUNT(*) as total,
                    SUM(CASE WHEN estado = 'firmado' THEN 1 ELSE 0 END) as firmados,
                    SUM(CASE WHEN estado = 'pendiente' THEN 1 ELSE 0 END) as pendientes,
                    SUM(CASE WHEN estado = 'expirado' THEN 1 ELSE 0 END) as expirados,
                    SUM(CASE WHEN estado = 'cancelado' THEN 1 ELSE 0 END) as cancelados
                 FROM contracts"
            );
        } else {
            $stats = $this->db->fetchOne(
                "SELECT
                    COUNT(*) as total,
                    SUM(CASE WHEN estado = 'firmado' THEN 1 ELSE 0 END) as firmados,
                    SUM(CASE WHEN estado = 'pendiente' THEN 1 ELSE 0 END) as pendientes,
                    SUM(CASE WHEN estado = 'expirado' THEN 1 ELSE 0 END) as expirados,
                    SUM(CASE WHEN estado = 'cancelado' THEN 1 ELSE 0 END) as cancelados
                 FROM contracts
                 WHERE admin_id = :admin_id",
                [':admin_id' => $adminId]
            );
        }

        return $stats ?: [
            'total' => 0,
            'firmados' => 0,
            'pendientes' => 0,
            'expirados' => 0,
            'cancelados' => 0
        ];
    }

    /**
     * Actualizar estado de contrato
     */
    public function updateStatus(int $contractId, string $newStatus): bool
    {
        $validStatuses = ['pendiente', 'firmado', 'expirado', 'cancelado'];
        if (!in_array($newStatus, $validStatuses)) {
            return false;
        }

        $updated = $this->db->update(
            'contracts',
            ['estado' => $newStatus],
            'id = :id',
            [':id' => $contractId]
        );

        if ($updated) {
            $this->logger->info("Contract #{$contractId} status updated to: {$newStatus}");
        }

        return $updated > 0;
    }

    /**
     * Cancelar contrato — solo administradores pueden cancelar
     */
    public function cancel(int $contractId, int $adminId, string $rol = 'asesor'): array
    {
        $contract = $this->getById($contractId);

        if (!$contract) {
            return [
                'success' => false,
                'message' => 'Contrato no encontrado.'
            ];
        }

        if ($rol !== 'administrador') {
            return [
                'success' => false,
                'message' => 'No tiene permisos para cancelar contratos.'
            ];
        }

        if ($contract['estado'] === 'firmado') {
            return [
                'success' => false,
                'message' => 'No se puede cancelar un contrato firmado.'
            ];
        }

        $this->updateStatus($contractId, 'cancelado');

        $this->logger->logAdminAccess(
            $adminId,
            'contract_cancelled',
            "Contrato cancelado: {$contract['titulo']}",
            ['contract_id' => $contractId]
        );

        return [
            'success' => true,
            'message' => 'Contrato cancelado exitosamente.'
        ];
    }

    /**
     * Verificar si un contrato puede ser firmado
     */
    public function canBeSigned(string $token): array
    {
        $contract = $this->getByToken($token);

        if (!$contract) {
            return [
                'can_sign' => false,
                'message' => 'El contrato no existe.'
            ];
        }

        if ($contract['estado'] === 'firmado') {
            return [
                'can_sign' => false,
                'message' => 'Este contrato ya ha sido firmado.'
            ];
        }

        if ($contract['estado'] === 'cancelado') {
            return [
                'can_sign' => false,
                'message' => 'Este contrato ha sido cancelado.'
            ];
        }

        // Verificar expiración
        if (strtotime($contract['expira_at']) < time()) {
            // Actualizar estado a expirado
            $this->updateStatus($contract['id'], 'expirado');

            return [
                'can_sign' => false,
                'message' => 'Este contrato ha expirado.'
            ];
        }

        return [
            'can_sign' => true,
            'contract' => $contract
        ];
    }

    /**
     * Marcar contratos expirados
     */
    public function markExpiredContracts(): int
    {
        $updated = $this->db->query(
            "UPDATE contracts
             SET estado = 'expirado'
             WHERE estado = 'pendiente' AND expira_at < NOW()"
        )->rowCount();

        if ($updated > 0) {
            $this->logger->info("Marked {$updated} contracts as expired");
        }

        return $updated;
    }

    /**
     * Eliminar contrato — solo administradores pueden eliminar
     */
    public function delete(int $contractId, int $adminId, string $rol = 'asesor'): array
    {
        $contract = $this->getById($contractId);

        if (!$contract) {
            return [
                'success' => false,
                'message' => 'Contrato no encontrado.'
            ];
        }

        if ($rol !== 'administrador') {
            return [
                'success' => false,
                'message' => 'No tiene permisos para eliminar contratos.'
            ];
        }

        if ($contract['estado'] === 'firmado') {
            return [
                'success' => false,
                'message' => 'No se puede eliminar un contrato firmado. Solo puede cancelarlo.'
            ];
        }

        // Eliminar (CASCADE eliminará firmas y intentos)
        $this->db->delete('contracts', 'id = :id', [':id' => $contractId]);

        $this->logger->logAdminAccess(
            $adminId,
            'contract_deleted',
            "Contrato eliminado: {$contract['titulo']}",
            ['contract_id' => $contractId]
        );

        return [
            'success' => true,
            'message' => 'Contrato eliminado exitosamente.'
        ];
    }
}
