<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Database;
use App\Core\Logger;
use App\Core\Request;
use App\Core\Session;
use App\Core\Tenant;
use App\Services\AuditService;
use App\Services\SupportTicketAlertEmailService;
use PDO;

class SupportController extends Controller
{
    public function index(Request $request): void
    {
        $companyId = (int) (Tenant::companyId() ?? 0);
        if ($companyId <= 0) {
            flash('error', 'Debes seleccionar una empresa para usar soporte.');
            redirect('empresas/seleccionar');
        }

        $pdo = Database::connection();
        $ticketsStmt = $pdo->prepare(
            'SELECT
                t.id,
                t.empresa_id,
                t.creado_por_usuario_id,
                uc.nombre AS creado_por,
                t.asignado_a_usuario_id,
                ua.nombre AS asignado_a,
                t.asunto,
                t.descripcion,
                t.prioridad,
                t.estado,
                t.canal,
                t.created_at,
                t.updated_at,
                t.primera_respuesta_at,
                t.primera_respuesta_limite_at,
                t.resolucion_limite_at,
                t.sla_primera_respuesta_estado,
                t.sla_resolucion_estado
             FROM soporte_tickets t
             LEFT JOIN usuarios uc ON uc.id = t.creado_por_usuario_id
             LEFT JOIN usuarios ua ON ua.id = t.asignado_a_usuario_id
             WHERE t.empresa_id = :empresa_id
             ORDER BY t.updated_at DESC, t.id DESC
             LIMIT 120'
        );
        $ticketsStmt->execute([':empresa_id' => $companyId]);
        $tickets = $ticketsStmt->fetchAll() ?: [];

        $eventsByTicket = [];
        $ticketIds = array_map('intval', array_column($tickets, 'id'));
        if ($ticketIds !== []) {
            $placeholders = implode(', ', array_fill(0, count($ticketIds), '?'));
            $eventsStmt = $pdo->prepare(
                'SELECT
                    te.id,
                    te.ticket_id,
                    te.usuario_id,
                    u.nombre AS usuario,
                    te.tipo,
                    te.mensaje,
                    te.estado_anterior,
                    te.estado_nuevo,
                    te.created_at
                 FROM soporte_ticket_eventos te
                 LEFT JOIN usuarios u ON u.id = te.usuario_id
                 WHERE te.ticket_id IN (' . $placeholders . ')
                   AND LOWER(COALESCE(te.tipo, "")) <> "interno"
                 ORDER BY te.id DESC'
            );
            $eventsStmt->execute($ticketIds);
            $events = $eventsStmt->fetchAll() ?: [];
            foreach ($events as $event) {
                $payload = support_event_message_decode((string) ($event['mensaje'] ?? ''));
                $event['mensaje_texto'] = (string) ($payload['text'] ?? '');
                $event['adjunto'] = $payload['attachment'] ?? null;
                $eventsByTicket[(int) ($event['ticket_id'] ?? 0)][] = $event;
            }
        }

        $this->render('soporte/index', [
            'title' => 'Soporte',
            'tickets' => $tickets,
            'events_by_ticket' => $eventsByTicket,
        ]);
    }

    public function storeTicket(Request $request): never
    {
        $companyId = (int) (Tenant::companyId() ?? 0);
        if ($companyId <= 0) {
            flash('error', 'Debes seleccionar una empresa para crear tickets.');
            redirect('empresas/seleccionar');
        }

        $subject = trim((string) $request->input('asunto', ''));
        $description = trim((string) $request->input('descripcion', ''));
        $priority = trim((string) $request->input('prioridad', 'media'));
        $allowedPriorities = $this->ticketPriorities();

        Session::setOldInput($request->all());

        if ($subject === '' || $description === '' || !in_array($priority, $allowedPriorities, true)) {
            flash('error', 'Completa asunto, descripción y prioridad válida.');
            redirect('soporte');
        }

        if (strlen($subject) > 180) {
            flash('error', 'El asunto no puede superar 180 caracteres.');
            redirect('soporte');
        }

        if (strlen($description) > 2000) {
            flash('error', 'La descripción no puede superar 2000 caracteres.');
            redirect('soporte');
        }

        try {
            $attachment = $this->storeAttachment($request->file('adjunto'));
        } catch (\Throwable $e) {
            flash('error', $e->getMessage());
            redirect('soporte');
        }

        $userId = (int) (Auth::id() ?? 0);
        $slaTargets = $this->ticketSlaTargets($priority);

        $createdTicket = Database::transaction(function (PDO $pdo) use ($companyId, $userId, $subject, $description, $priority, $slaTargets, $attachment): array {
            $createdAt = date('Y-m-d H:i:s');
            $firstResponseLimitAt = date(
                'Y-m-d H:i:s',
                strtotime($createdAt . ' +' . (int) ($slaTargets['first_response_minutes'] ?? 120) . ' minutes')
            );
            $resolutionLimitAt = date(
                'Y-m-d H:i:s',
                strtotime($createdAt . ' +' . (int) ($slaTargets['resolution_hours'] ?? 24) . ' hours')
            );

            $insertTicketStmt = $pdo->prepare(
                'INSERT INTO soporte_tickets (
                    empresa_id,
                    creado_por_usuario_id,
                    asunto,
                    descripcion,
                    prioridad,
                    estado,
                    canal,
                    created_at,
                    updated_at,
                    primera_respuesta_at,
                    primera_respuesta_limite_at,
                    resolucion_limite_at,
                    objetivo_primera_respuesta_min,
                    objetivo_resolucion_horas,
                    sla_primera_respuesta_estado,
                    sla_resolucion_estado,
                    cerrado_at,
                    resuelto_at
                ) VALUES (
                    :empresa_id,
                    :creado_por_usuario_id,
                    :asunto,
                    :descripcion,
                    :prioridad,
                    "abierto",
                    "panel_empresa",
                    :created_at,
                    :updated_at,
                    NULL,
                    :primera_respuesta_limite_at,
                    :resolucion_limite_at,
                    :objetivo_primera_respuesta_min,
                    :objetivo_resolucion_horas,
                    "pendiente",
                    "pendiente",
                    NULL,
                    NULL
                )'
            );
            $insertTicketStmt->execute([
                ':empresa_id' => $companyId,
                ':creado_por_usuario_id' => $userId > 0 ? $userId : null,
                ':asunto' => $subject,
                ':descripcion' => $description,
                ':prioridad' => $priority,
                ':created_at' => $createdAt,
                ':updated_at' => $createdAt,
                ':primera_respuesta_limite_at' => $firstResponseLimitAt,
                ':resolucion_limite_at' => $resolutionLimitAt,
                ':objetivo_primera_respuesta_min' => (int) ($slaTargets['first_response_minutes'] ?? 120),
                ':objetivo_resolucion_horas' => (int) ($slaTargets['resolution_hours'] ?? 24),
            ]);

            $ticketId = (int) $pdo->lastInsertId();
            $insertEventStmt = $pdo->prepare(
                'INSERT INTO soporte_ticket_eventos (
                    ticket_id, usuario_id, tipo, mensaje, estado_anterior, estado_nuevo, created_at
                ) VALUES (
                    :ticket_id, :usuario_id, "creacion", :mensaje, NULL, "abierto", NOW()
                )'
            );
            $insertEventStmt->execute([
                ':ticket_id' => $ticketId,
                ':usuario_id' => $userId > 0 ? $userId : null,
                ':mensaje' => support_event_message_encode($description, $attachment),
            ]);

            return [
                'ticket_id' => $ticketId,
                'created_at' => $createdAt,
            ];
        });

        Session::clearOldInput();
        AuditService::track('soporte.ticket.crear', 'Ticket creado por empresa ID: ' . $companyId);

        $newTicketId = (int) ($createdTicket['ticket_id'] ?? 0);
        if ($newTicketId > 0) {
            $companyName = 'Empresa';
            try {
                $companyStmt = Database::connection()->prepare('SELECT nombre FROM empresas WHERE id = :id LIMIT 1');
                $companyStmt->execute([':id' => $companyId]);
                $fetchedCompanyName = trim((string) ($companyStmt->fetchColumn() ?: ''));
                if ($fetchedCompanyName !== '') {
                    $companyName = $fetchedCompanyName;
                }
            } catch (\Throwable) {
                // Si falla, se envía con texto genérico.
            }

            $authUser = Auth::user();
            $createdBy = trim((string) ($authUser['nombre'] ?? 'Usuario empresa'));

            try {
                (new SupportTicketAlertEmailService())->notifySuperadminsNewTicket([
                    'ticket_id' => $newTicketId,
                    'empresa_id' => $companyId,
                    'empresa_nombre' => $companyName,
                    'asunto' => $subject,
                    'prioridad' => $priority,
                    'creado_por' => $createdBy,
                    'created_at' => (string) ($createdTicket['created_at'] ?? date('Y-m-d H:i:s')),
                    'panel_url' => url('superadmin') . '?seccion=soporte',
                ]);
            } catch (\Throwable $e) {
                Logger::info('No fue posible notificar ticket nuevo a superadmin por email', [
                    'ticket_id' => $newTicketId,
                    'empresa_id' => $companyId,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        flash('success', 'Ticket enviado. El equipo de soporte lo revisará pronto.');
        redirect('soporte');
    }

    public function addComment(Request $request): never
    {
        $companyId = (int) (Tenant::companyId() ?? 0);
        $ticketId = (int) $request->param('id', 0);
        $message = trim((string) $request->input('mensaje', ''));

        if ($companyId <= 0 || $ticketId <= 0) {
            flash('error', 'No se pudo identificar el ticket.');
            redirect('soporte');
        }

        try {
            $attachment = $this->storeAttachment($request->file('adjunto'));
        } catch (\Throwable $e) {
            flash('error', $e->getMessage());
            redirect('soporte');
        }

        if ($message === '' && $attachment === null) {
            flash('error', 'Escribe un comentario o adjunta una imagen.');
            redirect('soporte');
        }

        if (strlen($message) > 1500) {
            flash('error', 'El comentario no puede superar 1500 caracteres.');
            redirect('soporte');
        }

        $pdo = Database::connection();
        $ticketStmt = $pdo->prepare(
            'SELECT id, estado
             FROM soporte_tickets
             WHERE id = :id
               AND empresa_id = :empresa_id
             LIMIT 1'
        );
        $ticketStmt->execute([
            ':id' => $ticketId,
            ':empresa_id' => $companyId,
        ]);
        $ticket = $ticketStmt->fetch();
        if (!$ticket) {
            flash('error', 'Ticket no encontrado para tu empresa.');
            redirect('soporte');
        }

        $currentStatus = (string) ($ticket['estado'] ?? 'abierto');
        $shouldReopen = in_array($currentStatus, ['pendiente_cliente', 'resuelto', 'cerrado'], true);
        $newStatus = $shouldReopen ? 'abierto' : $currentStatus;
        $userId = (int) (Auth::id() ?? 0);

        Database::transaction(function (PDO $pdo) use ($ticketId, $companyId, $userId, $message, $attachment, $shouldReopen, $currentStatus, $newStatus): void {
            $insertStmt = $pdo->prepare(
                'INSERT INTO soporte_ticket_eventos (
                    ticket_id, usuario_id, tipo, mensaje, estado_anterior, estado_nuevo, created_at
                ) VALUES (
                    :ticket_id, :usuario_id, "comentario", :mensaje, NULL, NULL, NOW()
                )'
            );
            $insertStmt->execute([
                ':ticket_id' => $ticketId,
                ':usuario_id' => $userId > 0 ? $userId : null,
                ':mensaje' => support_event_message_encode($message, $attachment),
            ]);

            if ($shouldReopen) {
                $reopenStmt = $pdo->prepare(
                    'UPDATE soporte_tickets
                     SET estado = "abierto",
                         cerrado_at = NULL,
                         resuelto_at = NULL,
                         sla_resolucion_estado = CASE
                            WHEN resolucion_limite_at IS NOT NULL AND NOW() > resolucion_limite_at
                                THEN "incumplido"
                            ELSE "pendiente"
                         END,
                         updated_at = NOW()
                     WHERE id = :id
                       AND empresa_id = :empresa_id'
                );
                $reopenStmt->execute([
                    ':id' => $ticketId,
                    ':empresa_id' => $companyId,
                ]);

                $statusEventStmt = $pdo->prepare(
                    'INSERT INTO soporte_ticket_eventos (
                        ticket_id, usuario_id, tipo, mensaje, estado_anterior, estado_nuevo, created_at
                    ) VALUES (
                        :ticket_id, :usuario_id, "estado", :mensaje, :estado_anterior, :estado_nuevo, NOW()
                    )'
                );
                $statusEventStmt->execute([
                    ':ticket_id' => $ticketId,
                    ':usuario_id' => $userId > 0 ? $userId : null,
                    ':mensaje' => 'Cliente respondió y reabrió el ticket.',
                    ':estado_anterior' => $currentStatus,
                    ':estado_nuevo' => $newStatus,
                ]);
                return;
            }

            $touchStmt = $pdo->prepare(
                'UPDATE soporte_tickets
                 SET updated_at = NOW()
                 WHERE id = :id
                   AND empresa_id = :empresa_id'
            );
            $touchStmt->execute([
                ':id' => $ticketId,
                ':empresa_id' => $companyId,
            ]);
        });

        AuditService::track('soporte.ticket.comentario', 'Comentario empresa en ticket ID: ' . $ticketId);
        flash('success', 'Comentario enviado a soporte.');
        redirect('soporte');
    }

    private function ticketPriorities(): array
    {
        return ['baja', 'media', 'alta', 'critica'];
    }

    private function ticketSlaTargets(string $priority): array
    {
        $defaults = [
            'first_response_minutes' => 120,
            'resolution_hours' => 24,
        ];
        $configByPriority = config('support.sla.' . $priority, []);

        return [
            'first_response_minutes' => max(
                1,
                (int) ($configByPriority['first_response_minutes'] ?? $defaults['first_response_minutes'])
            ),
            'resolution_hours' => max(
                1,
                (int) ($configByPriority['resolution_hours'] ?? $defaults['resolution_hours'])
            ),
        ];
    }

    private function storeAttachment(mixed $file): ?array
    {
        if (!is_array($file)) {
            return null;
        }

        $errorCode = (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE);
        if ($errorCode === UPLOAD_ERR_NO_FILE) {
            return null;
        }

        if ($errorCode !== UPLOAD_ERR_OK) {
            throw new \RuntimeException('No fue posible cargar la imagen adjunta.');
        }

        $tmpPath = (string) ($file['tmp_name'] ?? '');
        $size = (int) ($file['size'] ?? 0);
        $originalName = (string) ($file['name'] ?? 'adjunto');

        if ($tmpPath === '' || !is_file($tmpPath) || $size <= 0) {
            throw new \RuntimeException('La imagen adjunta no es válida.');
        }

        $maxSize = 5 * 1024 * 1024;
        if ($size > $maxSize) {
            throw new \RuntimeException('La imagen supera el tamaño máximo de 5MB.');
        }

        $extension = strtolower((string) pathinfo($originalName, PATHINFO_EXTENSION));
        $allowedExtensions = ['jpg', 'jpeg', 'png', 'webp', 'gif'];
        if (!in_array($extension, $allowedExtensions, true)) {
            throw new \RuntimeException('Formato no permitido. Usa JPG, PNG, WEBP o GIF.');
        }

        $mimeType = function_exists('mime_content_type')
            ? strtolower((string) (mime_content_type($tmpPath) ?: ''))
            : '';
        $allowedMimeTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
        if ($mimeType !== '' && !in_array($mimeType, $allowedMimeTypes, true)) {
            throw new \RuntimeException('El archivo adjunto no parece una imagen válida.');
        }

        $uploadDir = base_path('public/uploads/support');
        if (!is_dir($uploadDir) && !mkdir($uploadDir, 0777, true) && !is_dir($uploadDir)) {
            throw new \RuntimeException('No se pudo crear la carpeta de adjuntos de soporte.');
        }

        try {
            $token = bin2hex(random_bytes(8));
        } catch (\Throwable) {
            $token = (string) mt_rand(10000000, 99999999);
        }

        $fileName = 'support_' . date('Ymd_His') . '_' . $token . '.' . $extension;
        $targetPath = $uploadDir . '/' . $fileName;

        if (!move_uploaded_file($tmpPath, $targetPath)) {
            if (!rename($tmpPath, $targetPath)) {
                throw new \RuntimeException('No fue posible guardar la imagen en el servidor.');
            }
        }

        $safeOriginalName = preg_replace('/[^A-Za-z0-9._-]+/', '_', $originalName) ?? '';
        $safeOriginalName = trim($safeOriginalName, '._-');
        if ($safeOriginalName === '') {
            $safeOriginalName = 'adjunto.' . $extension;
        }

        return [
            'path' => 'uploads/support/' . $fileName,
            'name' => $safeOriginalName,
            'mime' => $mimeType !== '' ? $mimeType : 'image/' . $extension,
            'size_kb' => (int) ceil($size / 1024),
        ];
    }
}
