import { z } from 'zod';
import { logger } from '../../config/logger';
import { buildFinalAnswerPrompt, buildPlanningPrompt, outOfScopeMessage } from '../../prompts/systemPrompt';
import { ToolRegistry } from '../../tools/ToolRegistry';
import type { ToolCall, ToolExecutionContext, ToolResult } from '../../tools/types';
import { AppError } from '../../utils/AppError';
import { parseJsonObject } from '../../utils/json';
import { OllamaClient } from './OllamaClient';

const plannerResponseSchema = z.object({
  inScope: z.boolean(),
  toolCalls: z
    .array(
      z.object({
        name: z.string().min(1),
        arguments: z.unknown().default({}),
      }),
    )
    .default([]),
  finalAnswer: z.string().nullable().optional(),
});

export interface AgentRequest {
  message: string;
  context?: Record<string, unknown>;
  maxSteps?: number;
}

export interface AgentResponse {
  answer: string;
  toolCalls: ToolCall[];
  toolResults: ToolResult[];
}

export class AgentService {
  constructor(
    private readonly ollama = new OllamaClient(),
    private readonly registry = new ToolRegistry(),
  ) {}

  async handle(request: AgentRequest, executionContext: ToolExecutionContext): Promise<AgentResponse> {
    const startedAt = Date.now();
    const tools = this.registry.list();
    const planningPrompt = buildPlanningPrompt(tools);
    const userContent = JSON.stringify({
      message: request.message,
      authenticatedUser: {
        nome: executionContext.user.nome,
        email: executionContext.user.email,
      },
      context: request.context ?? {},
    });

    const plannerContent = await this.ollama.chat([
      { role: 'system', content: planningPrompt },
      { role: 'user', content: userContent },
    ]);

    let plannerResponse: z.infer<typeof plannerResponseSchema>;
    try {
      plannerResponse = plannerResponseSchema.parse(parseJsonObject(plannerContent));
    } catch (error) {
      throw new AppError('Could not parse model tool decision.', 502, 'INVALID_MODEL_TOOL_DECISION', {
        content: plannerContent,
        error: error instanceof Error ? error.message : error,
      });
    }

    if (!plannerResponse.inScope) {
      const answer = plannerResponse.finalAnswer || outOfScopeMessage;
      logger.info({ durationMs: Date.now() - startedAt, answer }, 'Out-of-scope request blocked');
      return {
        answer,
        toolCalls: [],
        toolResults: [],
      };
    }

    const maxSteps = request.maxSteps ?? 5;
    const toolCalls = plannerResponse.toolCalls.slice(0, maxSteps);
    const toolResults: ToolResult[] = [];

    if (toolCalls.length === 0) {
      const answer = plannerResponse.finalAnswer || 'Preciso de mais informacoes para executar essa solicitacao.';
      return {
        answer,
        toolCalls: [],
        toolResults,
      };
    }

    for (const call of toolCalls) {
      toolResults.push(await this.registry.execute(call, executionContext));
    }

    const finalAnswer = await this.ollama.chat([
      {
        role: 'system',
        content:
          'Voce e o agente financeiro do Finance App. Interprete a solicitacao, use os resultados das tools como fonte de verdade, faca os calculos necessarios e responda como assistente financeiro, nao como uma tela de listagem.',
      },
      { role: 'user', content: buildFinalAnswerPrompt(request.message, toolResults) },
    ]);

    logger.info(
      {
        durationMs: Date.now() - startedAt,
        toolCalls,
        answer: finalAnswer,
      },
      'Agent response generated',
    );

    return {
      answer: finalAnswer,
      toolCalls,
      toolResults,
    };
  }
}
