<?php

namespace App\Services;

use App\AI\Prompts\SystemPromptAdmin;
use App\AI\Prompts\SystemPromptPublic;
use App\AI\Tools\CargarPuntajeTool;
use App\AI\Tools\CrearPartidoTool;
use App\AI\Tools\EliminarNoticiaTool;
use App\AI\Tools\EliminarPartidoTool;
use App\AI\Tools\ListarEquiposTool;
use App\AI\Tools\ListarEventosTool;
use App\AI\Tools\ListarTorneosTool;
use App\AI\Tools\RedactarNoticiaTool;
use App\Models\AgentThread;
use Illuminate\Support\Facades\Log;
use Prism\Prism\Exceptions\PrismRateLimitedException;
use Prism\Prism\Facades\Prism;
use Prism\Prism\Facades\Tool;
use Prism\Prism\ValueObjects\Messages\AssistantMessage;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Throwable;

class GeniusAgentService
{
    private const PROVIDER  = 'gemini';
    private const MAX_STEPS = 6;

    private function model(): string
    {
        return config('services.genius.model', 'gemini-2.5-flash-lite');
    }

    public function chatPublic(string $message, array $history): string
    {
        $messages = $this->buildMessages($history, $message);

        try {
            $response = Prism::text()
                ->using(self::PROVIDER, $this->model())
                ->withSystemPrompt(SystemPromptPublic::get())
                ->withMessages($messages)
                ->asText();

            return $response->text;
        } catch (PrismRateLimitedException $e) {
            report($e);
            return 'Demasiadas consultas en poco tiempo. Por favor, esperá un momento e intentá de nuevo.';
        } catch (Throwable $e) {
            report($e);
            return 'El agente no responde en este momento. Por favor, intentá de nuevo en unos instantes.';
        }
    }

    public function chatAdmin(string $message, AgentThread $thread, bool $isSuperadmin = false): string
    {
        $messages = $this->buildMessages($thread->messages ?? [], $message);

        try {
            $response = Prism::text()
                ->using(self::PROVIDER, $this->model())
                ->withSystemPrompt(SystemPromptAdmin::get($isSuperadmin))
                ->withMessages($messages)
                ->withTools($this->getAdminTools($isSuperadmin))
                ->withMaxSteps(self::MAX_STEPS)
                ->asText();

            if ($response->text !== '') {
                $reply = $response->text;
            } else {
                Log::warning('Genius admin: empty text from model', [
                    'finishReason' => isset($response->finishReason)
                        ? (is_object($response->finishReason) ? ($response->finishReason->name ?? get_class($response->finishReason)) : $response->finishReason)
                        : null,
                    'steps_count'  => is_countable($response->steps ?? null) ? count($response->steps) : null,
                    'tool_calls'   => is_countable($response->toolCalls ?? null) ? count($response->toolCalls) : null,
                    'tool_results' => is_countable($response->toolResults ?? null) ? count($response->toolResults) : null,
                ]);
                $reply = $this->fallbackFromToolResults($response);
            }

            $updated = array_merge($thread->messages ?? [], [
                ['role' => 'user',      'content' => $message],
                ['role' => 'assistant', 'content' => $reply],
            ]);

            $thread->update(['messages' => $updated]);

            return $reply;
        } catch (PrismRateLimitedException $e) {
            report($e);
            return 'Demasiadas consultas en poco tiempo. Por favor, esperá un momento e intentá de nuevo.';
        } catch (Throwable $e) {
            report($e);
            return 'El agente no responde en este momento. Por favor, intentá de nuevo en unos instantes.';
        }
    }

    private function fallbackFromToolResults($response): string
    {
        $collected = [];

        $steps = $response->steps ?? [];
        foreach ($steps as $step) {
            if (!empty($step->text ?? '')) {
                return $step->text;
            }

            foreach (($step->toolResults ?? []) as $tr) {
                $collected[] = [
                    'tool'   => $tr->toolName ?? ($tr->name ?? 'tool'),
                    'result' => $tr->result ?? null,
                ];
            }
        }

        if (empty($collected)) {
            foreach (($response->toolResults ?? []) as $tr) {
                $collected[] = [
                    'tool'   => $tr->toolName ?? ($tr->name ?? 'tool'),
                    'result' => $tr->result ?? null,
                ];
            }
        }

        if (empty($collected)) {
            return 'El agente no devolvió una respuesta. Revisá storage/logs/laravel.log para el detalle.';
        }

        $lines = ["Datos obtenidos (la IA no generó un resumen en texto):"];
        foreach ($collected as $item) {
            $decoded = is_string($item['result']) ? json_decode($item['result'], true) : $item['result'];
            $pretty  = is_array($decoded) || is_object($decoded)
                ? json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
                : (string) $item['result'];
            $lines[] = "\n**{$item['tool']}**:\n{$pretty}";
        }

        return implode("\n", $lines);
    }

    /** @return array<int, \Prism\Prism\Contracts\Message> */
    private function buildMessages(array $history, string $newMessage): array
    {
        $limit = (int) config('services.genius.history_limit', 10);
        $trimmed = $limit > 0 ? array_slice($history, -$limit * 2) : $history;

        $messages = [];

        foreach ($trimmed as $entry) {
            if (($entry['role'] ?? '') === 'user') {
                $messages[] = new UserMessage($entry['content']);
            } elseif (($entry['role'] ?? '') === 'assistant') {
                $messages[] = new AssistantMessage($entry['content']);
            }
        }

        $messages[] = new UserMessage($newMessage);

        return $messages;
    }

    /** @return array<int, \Prism\Prism\Tool> */
    private function getAdminTools(bool $isSuperadmin): array
    {
        $readOnly = [
            Tool::as('listar_torneos')
                ->for('Lista todos los torneos del sistema con sus IDs y fechas. Úsalo cuando el admin mencione un torneo por nombre para obtener el id_torneo.')
                ->using(new ListarTorneosTool()),

            Tool::as('listar_equipos')
                ->for('Lista los equipos del sistema. Acepta filtros opcionales por torneo (id_torneo) y grupo.')
                ->withNumberParameter('id_torneo', 'ID del torneo para filtrar equipos', false)
                ->withStringParameter('grupo', 'Nombre del grupo (ej. "A", "B") para filtrar dentro del torneo', false)
                ->using(new ListarEquiposTool()),

            Tool::as('listar_eventos')
                ->for('Lista los partidos/eventos del sistema. Acepta filtros opcionales por rango de fechas y torneo.')
                ->withStringParameter('fecha_desde', 'Fecha de inicio del rango (formato YYYY-MM-DD)', false)
                ->withStringParameter('fecha_hasta', 'Fecha de fin del rango (formato YYYY-MM-DD)', false)
                ->withNumberParameter('id_torneo', 'ID del torneo para filtrar eventos', false)
                ->using(new ListarEventosTool()),
        ];

        if (!$isSuperadmin) {
            return $readOnly;
        }

        $writeTools = [
            Tool::as('crear_partido')
                ->for('Crea un nuevo partido en el sistema. SOLO ejecutar tras confirmación explícita del superadmin.')
                ->withNumberParameter('id_equipo_local', 'ID del equipo local')
                ->withNumberParameter('id_equipo_visitante', 'ID del equipo visitante')
                ->withStringParameter('fecha_evento', 'Fecha del partido (formato YYYY-MM-DD)')
                ->withStringParameter('hora_inicio', 'Hora de inicio (formato HH:MM)')
                ->withStringParameter('hora_fin', 'Hora de fin (formato HH:MM)')
                ->withStringParameter('sede', 'Nombre de la cancha o sede del partido')
                ->withNumberParameter('id_torneo', 'ID del torneo al que pertenece el partido')
                ->using(new CrearPartidoTool()),

            Tool::as('cargar_puntaje')
                ->for('Actualiza el marcador de un partido existente. SOLO ejecutar tras confirmación explícita del superadmin.')
                ->withStringParameter('id_evento', 'UUID del partido a actualizar')
                ->withNumberParameter('marcador_local', 'Puntos del equipo local')
                ->withNumberParameter('marcador_visitante', 'Puntos del equipo visitante')
                ->using(new CargarPuntajeTool()),

            Tool::as('redactar_noticia')
                ->for('Publica una noticia en el portal OnAPB. SOLO ejecutar tras confirmación explícita del superadmin.')
                ->withStringParameter('titulo', 'Título de la noticia')
                ->withStringParameter('contenido', 'Cuerpo completo de la noticia en HTML o texto plano')
                ->withNumberParameter('id_torneo', 'ID del torneo relacionado (opcional)', false)
                ->withStringParameter('categoria', 'Categoría de la noticia (opcional)', false)
                ->using(new RedactarNoticiaTool()),

            Tool::as('eliminar_noticia')
                ->for('Elimina (rollback) una noticia publicada. Usar cuando el superadmin pida deshacer una creación.')
                ->withNumberParameter('id_noticia', 'ID numérico de la noticia a eliminar')
                ->using(new EliminarNoticiaTool()),

            Tool::as('eliminar_partido')
                ->for('Elimina (soft delete / rollback) un partido creado. Usar cuando el superadmin pida deshacer.')
                ->withStringParameter('id_evento', 'UUID del partido a eliminar')
                ->using(new EliminarPartidoTool()),
        ];

        return array_merge($readOnly, $writeTools);
    }
}
