<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Database;
use App\Core\Request;
use App\Core\Response;
use App\Core\Session;
use App\Core\Tenant;
use App\Models\AiHelpMessage;
use App\Models\AiHelpSession;
use App\Services\AiHelpService;
use App\Services\AuditService;
use App\Services\OpenAiGovernanceService;

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

        $userId = (int) (Auth::id() ?? 0);
        if ($userId <= 0) {
            flash('error', 'Tu sesión expiró. Inicia sesión de nuevo.');
            redirect('login');
        }

        $companyName = $this->companyName($companyId);
        $assistantConfig = (new AiHelpService())->configSnapshot();
        $idleTimeoutSeconds = $this->idleTimeoutSeconds();

        $sessionStore = new AiHelpSession();
        $messageStore = new AiHelpMessage();
        $selectedClosedSessionId = max(0, (int) $request->query('chat_cerrado', 0));

        $currentSession = $this->resolveCurrentSession($companyId, $userId, $sessionStore);
        $chatClosed = false;

        if (is_array($currentSession)
            && strtolower((string) ($currentSession['estado'] ?? '')) === 'activa'
            && $this->isChatExpired($companyId, $userId, $idleTimeoutSeconds)
        ) {
            $sessionStore->close((int) ($currentSession['id'] ?? 0));
            $chatClosed = true;
            $currentSession['estado'] = 'cerrada';
        }

        if (!$chatClosed && !is_array($currentSession) && $selectedClosedSessionId <= 0) {
            $currentSession = $this->createAndSelectSession($companyId, $userId, $sessionStore);
        }

        $viewingClosedSessionId = 0;
        $viewSession = $currentSession;

        if ($selectedClosedSessionId > 0) {
            $selectedSession = $sessionStore->findOwned($selectedClosedSessionId, $companyId, $userId);
            if (is_array($selectedSession) && strtolower((string) ($selectedSession['estado'] ?? '')) === 'cerrada') {
                $viewSession = $selectedSession;
                $viewingClosedSessionId = (int) ($selectedSession['id'] ?? 0);
                $chatClosed = true;
            }
        }

        $messages = [];
        if (is_array($viewSession)) {
            try {
                $messages = $messageStore->recentBySession(
                    (int) ($viewSession['id'] ?? 0),
                    $companyId,
                    $userId,
                    $this->historyLimit()
                );
            } catch (\Throwable) {
                $messages = [];
            }
        }

        if (!$chatClosed && is_array($currentSession)) {
            $this->touchChatSession($companyId, $userId);
            $sessionStore->touch((int) ($currentSession['id'] ?? 0));
        }

        $closedChats = [];
        try {
            $closedChats = $sessionStore->closedByUserCompany($companyId, $userId, 12);
        } catch (\Throwable) {
            $closedChats = [];
        }

        $currentSessionId = is_array($viewSession) ? (int) ($viewSession['id'] ?? 0) : 0;

        $this->render('soporte/ia', [
            'title' => 'Ayuda IA',
            'assistant_config' => $assistantConfig,
            'company_name' => $companyName,
            'company_id' => $companyId,
            'messages' => $messages,
            'chat_closed' => $chatClosed,
            'idle_timeout_seconds' => $idleTimeoutSeconds,
            'closed_chats' => $closedChats,
            'viewing_closed_session_id' => $viewingClosedSessionId,
            'current_session_id' => $currentSessionId,
        ]);
    }

    public function ask(Request $request): never
    {
        $companyId = (int) (Tenant::companyId() ?? 0);
        $userId = (int) (Auth::id() ?? 0);
        if ($companyId <= 0 || $userId <= 0) {
            Response::json([
                'ok' => false,
                'message' => 'Sesión inválida para consultar el asistente.',
            ], 401);
        }

        $idleTimeoutSeconds = $this->idleTimeoutSeconds();
        $sessionStore = new AiHelpSession();
        $messageStore = new AiHelpMessage();

        $currentSession = $this->resolveCurrentSession($companyId, $userId, $sessionStore);
        if (!is_array($currentSession)) {
            $currentSession = $this->createAndSelectSession($companyId, $userId, $sessionStore);
        }

        $currentSessionId = (int) ($currentSession['id'] ?? 0);
        if ($currentSessionId <= 0) {
            Response::json([
                'ok' => false,
                'message' => 'No fue posible inicializar la sesión del chat.',
            ], 500);
        }

        if ($this->isChatExpired($companyId, $userId, $idleTimeoutSeconds)) {
            $sessionStore->close($currentSessionId);
            Response::json([
                'ok' => false,
                'code' => 'chat_idle_timeout',
                'message' => 'El chat se cerró por inactividad. Pulsa "Reabrir chat" para continuar.',
            ], 440);
        }

        $question = trim((string) $request->input('pregunta', ''));
        if ($question === '') {
            Response::json([
                'ok' => false,
                'message' => 'Escribe una pregunta para continuar.',
            ], 422);
        }
        if ($this->stringLength($question) > 1200) {
            Response::json([
                'ok' => false,
                'message' => 'La pregunta no puede superar 1200 caracteres.',
            ], 422);
        }

        $governance = new OpenAiGovernanceService();
        $guard = $governance->guardForCompany($companyId);
        if (!(bool) ($guard['ok'] ?? false)) {
            Response::json([
                'ok' => false,
                'code' => (string) ($guard['code'] ?? 'openai_limit_reached'),
                'message' => trim((string) ($guard['message'] ?? 'Se alcanzó un límite de uso del asistente IA.')),
            ], 429);
        }

        $user = Auth::user() ?? [];
        $companyName = $this->companyName($companyId);

        $this->touchChatSession($companyId, $userId);
        $sessionStore->touch($currentSessionId);
        $sessionStore->updateTitleIfGeneric($currentSessionId, $question);

        $history = [];
        try {
            $history = $messageStore->contextForPromptBySession(
                $currentSessionId,
                $companyId,
                $userId,
                $this->contextHistoryLimit()
            );
        } catch (\Throwable) {
            $history = [];
        }

        try {
            $messageStore->add($companyId, $userId, $currentSessionId, 'user', $question);
        } catch (\Throwable) {
            // Continúa aunque falle el guardado del historial.
        }

        $result = (new AiHelpService())->ask($question, [
            'user_name' => trim((string) ($user['nombre'] ?? 'Operador')),
            'role_name' => trim((string) ($user['role_name'] ?? '')),
            'company_id' => $companyId > 0 ? (string) $companyId : 'N/D',
            'company_name' => $companyName !== '' ? $companyName : 'N/D',
            'current_path' => (string) ($request->server('REQUEST_URI', '') ?: ''),
            'history' => $history,
        ]);

        if (!(bool) ($result['ok'] ?? false)) {
            $message = trim((string) ($result['error'] ?? 'No fue posible responder en este momento.'));
            Response::json([
                'ok' => false,
                'message' => $message !== '' ? $message : 'No fue posible responder en este momento.',
            ], 422);
        }

        $answer = (string) ($result['answer'] ?? '');
        $model = (string) ($result['model'] ?? '');
        try {
            $messageStore->add($companyId, $userId, $currentSessionId, 'assistant', $answer, $model, [
                'usage' => is_array($result['usage'] ?? null) ? $result['usage'] : [],
            ]);
        } catch (\Throwable) {
            // Continúa aunque falle el guardado del historial.
        }

        $this->touchChatSession($companyId, $userId);
        $sessionStore->touch($currentSessionId);
        AuditService::track('ai.help.ask', 'Consulta IA realizada por usuario ID: ' . (string) (Auth::id() ?? 0));

        Response::json([
            'ok' => true,
            'answer' => $answer,
            'model' => $model,
            'chat_expires_in_seconds' => $idleTimeoutSeconds,
            'chat_expires_at' => date('c', time() + $idleTimeoutSeconds),
        ]);
    }

    public function reopen(Request $request): never
    {
        $companyId = (int) (Tenant::companyId() ?? 0);
        $userId = (int) (Auth::id() ?? 0);
        if ($companyId <= 0 || $userId <= 0) {
            Response::json([
                'ok' => false,
                'message' => 'Sesión inválida para reabrir chat.',
            ], 401);
        }

        $expectsJson = $this->expectsJson($request);
        $sessionStore = new AiHelpSession();
        $targetSessionId = max(0, (int) $request->input('session_id', 0));

        if ($targetSessionId <= 0) {
            $targetSessionId = $this->currentSessionId($companyId, $userId);
        }

        $targetSession = null;
        if ($targetSessionId > 0) {
            $targetSession = $sessionStore->findOwned($targetSessionId, $companyId, $userId);
        }

        if (!is_array($targetSession)) {
            $targetSession = $this->createAndSelectSession($companyId, $userId, $sessionStore);
        } else {
            $targetSessionId = (int) ($targetSession['id'] ?? 0);
            $sessionStore->closeAllActiveByUserCompany($companyId, $userId, $targetSessionId);
            $sessionStore->reopen($targetSessionId);
            $this->setCurrentSessionId($companyId, $userId, $targetSessionId);
        }

        $idleTimeoutSeconds = $this->idleTimeoutSeconds();
        $this->touchChatSession($companyId, $userId);

        if (!$expectsJson) {
            flash('success', 'Chat reabierto correctamente.');
            redirect('ayuda-ia');
        }

        Response::json([
            'ok' => true,
            'message' => 'Chat reabierto.',
            'chat_expires_in_seconds' => $idleTimeoutSeconds,
            'chat_expires_at' => date('c', time() + $idleTimeoutSeconds),
        ]);
    }

    private function companyName(int $companyId): string
    {
        if ($companyId <= 0) {
            return '';
        }

        $stmt = Database::connection()->prepare(
            'SELECT nombre FROM empresas WHERE id = :id LIMIT 1'
        );
        $stmt->execute([':id' => $companyId]);
        return trim((string) ($stmt->fetchColumn() ?: ''));
    }

    private function idleTimeoutSeconds(): int
    {
        $minutes = (int) config('ai.chat.idle_timeout_minutes', 5);
        return max(60, min(3600, $minutes * 60));
    }

    private function historyLimit(): int
    {
        $limit = (int) config('ai.chat.history_limit', 80);
        return max(20, min(200, $limit));
    }

    private function contextHistoryLimit(): int
    {
        $limit = (new OpenAiGovernanceService())->contextHistoryLimit();
        return max(4, min(30, $limit));
    }

    private function chatLastActivityKey(int $companyId, int $userId): string
    {
        return '_ai_chat_last_activity_' . $companyId . '_' . $userId;
    }

    private function chatCurrentSessionKey(int $companyId, int $userId): string
    {
        return '_ai_chat_current_session_' . $companyId . '_' . $userId;
    }

    private function currentSessionId(int $companyId, int $userId): int
    {
        return (int) Session::get($this->chatCurrentSessionKey($companyId, $userId), 0);
    }

    private function setCurrentSessionId(int $companyId, int $userId, int $sessionId): void
    {
        Session::put($this->chatCurrentSessionKey($companyId, $userId), $sessionId);
    }

    private function touchChatSession(int $companyId, int $userId): void
    {
        Session::put($this->chatLastActivityKey($companyId, $userId), time());
    }

    private function isChatExpired(int $companyId, int $userId, int $timeoutSeconds): bool
    {
        $lastActivity = (int) Session::get($this->chatLastActivityKey($companyId, $userId), 0);
        if ($lastActivity <= 0) {
            return false;
        }

        return (time() - $lastActivity) >= max(60, $timeoutSeconds);
    }

    private function resolveCurrentSession(int $companyId, int $userId, AiHelpSession $sessionStore): ?array
    {
        $currentId = $this->currentSessionId($companyId, $userId);
        if ($currentId > 0) {
            $session = $sessionStore->findOwned($currentId, $companyId, $userId);
            if (is_array($session)) {
                return $session;
            }
        }

        $active = $sessionStore->activeByUserCompany($companyId, $userId);
        if (is_array($active)) {
            $this->setCurrentSessionId($companyId, $userId, (int) ($active['id'] ?? 0));
            return $active;
        }

        return null;
    }

    private function createAndSelectSession(int $companyId, int $userId, AiHelpSession $sessionStore): ?array
    {
        $sessionStore->closeAllActiveByUserCompany($companyId, $userId);
        $created = $sessionStore->createActive($companyId, $userId);
        $sessionId = (int) ($created['id'] ?? 0);
        if ($sessionId > 0) {
            $this->setCurrentSessionId($companyId, $userId, $sessionId);
        }

        return is_array($created) ? $created : null;
    }

    private function expectsJson(Request $request): bool
    {
        $accept = strtolower((string) $request->server('HTTP_ACCEPT', ''));
        $requestedWith = strtolower((string) $request->server('HTTP_X_REQUESTED_WITH', ''));
        return str_contains($accept, 'application/json') || $requestedWith === 'xmlhttprequest';
    }

    private function stringLength(string $value): int
    {
        if (function_exists('mb_strlen')) {
            return mb_strlen($value);
        }

        return strlen($value);
    }
}
