/**
 * Chat Session Router — send messages, get session, close session
 */

import { TRPCError } from '@trpc/server';
import { z } from 'zod';
import type { ProcedureBuilder } from '../trpc.js';
import {
  requireAgentManager,
  requireInitiativeRepository,
  requirePhaseRepository,
  requirePageRepository,
  requireTaskRepository,
  requireChatSessionRepository,
} from './_helpers.js';
import { gatherInitiativeContext } from './architect.js';
import { buildChatPrompt, type ChatHistoryEntry } from '../../agent/prompts/index.js';

const MAX_HISTORY_MESSAGES = 30;

export function chatSessionProcedures(publicProcedure: ProcedureBuilder) {
  return {
    sendChatMessage: publicProcedure
      .input(z.object({
        targetType: z.enum(['phase', 'task']),
        targetId: z.string().min(1),
        initiativeId: z.string().min(1),
        message: z.string().min(1),
        retry: z.boolean().optional(),
        provider: z.string().optional(),
      }))
      .mutation(async ({ ctx, input }) => {
        const agentManager = requireAgentManager(ctx);
        const chatRepo = requireChatSessionRepository(ctx);
        const initiativeRepo = requireInitiativeRepository(ctx);
        const taskRepo = requireTaskRepository(ctx);

        const initiative = await initiativeRepo.findById(input.initiativeId);
        if (!initiative) {
          throw new TRPCError({ code: 'NOT_FOUND', message: `Initiative '${input.initiativeId}' not found` });
        }

        // Find or create active session
        let session = await chatRepo.findActiveSession(input.targetType, input.targetId);
        if (!session) {
          session = await chatRepo.createSession({
            targetType: input.targetType,
            targetId: input.targetId,
            initiativeId: input.initiativeId,
          });
        }

        // Store user message (skip on retry — message already exists)
        if (!input.retry) {
          await chatRepo.createMessage({
            chatSessionId: session.id,
            role: 'user',
            content: input.message,
          });

          ctx.eventBus.emit({
            type: 'chat:message_created' as const,
            timestamp: new Date(),
            payload: { chatSessionId: session.id, role: 'user' as const },
          });
        }

        // Check if agent exists and is waiting for input
        if (session.agentId) {
          const agent = await agentManager.get(session.agentId);
          if (agent && agent.status === 'waiting_for_input') {
            // Resume the existing agent
            await agentManager.resume(agent.id, { 'chat-response': input.message });
            return { sessionId: session.id, agentId: agent.id, action: 'resumed' as const };
          }

          // Agent exists but not waiting — dismiss it
          if (agent && !agent.userDismissedAt) {
            if (['running'].includes(agent.status)) {
              await agentManager.stop(agent.id);
            }
            await agentManager.dismiss(agent.id);
          }
        }

        // Spawn fresh agent with chat history + context
        const messages = await chatRepo.findMessagesBySessionId(session.id);
        const chatHistory: ChatHistoryEntry[] = messages
          .slice(-MAX_HISTORY_MESSAGES)
          .map(m => ({ role: m.role as 'user' | 'assistant' | 'system', content: m.content }));

        const context = await gatherInitiativeContext(
          ctx.phaseRepository,
          ctx.taskRepository,
          ctx.pageRepository,
          input.initiativeId,
        );

        const prompt = buildChatPrompt(input.targetType, input.targetId, chatHistory, input.message);

        // Create a task for the chat agent
        const targetName = input.targetType === 'phase'
          ? (await ctx.phaseRepository?.findById(input.targetId))?.name ?? input.targetId
          : (await ctx.taskRepository?.findById(input.targetId))?.name ?? input.targetId;

        const task = await taskRepo.create({
          initiativeId: input.initiativeId,
          name: `Chat: ${targetName}`,
          description: `Iterative chat refinement of ${input.targetType}`,
          category: 'discuss',
          status: 'in_progress',
        });

        // Determine target phase/task for input context
        const targetPhase = input.targetType === 'phase'
          ? await ctx.phaseRepository?.findById(input.targetId)
          : undefined;
        const targetTask = input.targetType === 'task'
          ? await ctx.taskRepository?.findById(input.targetId)
          : undefined;

        const agent = await agentManager.spawn({
          taskId: task.id,
          prompt,
          mode: 'chat',
          provider: input.provider,
          initiativeId: input.initiativeId,
          inputContext: {
            initiative,
            phase: targetPhase ?? undefined,
            task: targetTask ?? undefined,
            pages: context.pages.length > 0 ? context.pages : undefined,
            phases: context.phases.length > 0 ? context.phases : undefined,
            tasks: context.tasks.length > 0 ? context.tasks : undefined,
          },
        });

        // Link agent to session
        await chatRepo.updateSession(session.id, { agentId: agent.id });

        return { sessionId: session.id, agentId: agent.id, action: 'spawned' as const };
      }),

    getChatSession: publicProcedure
      .input(z.object({
        targetType: z.enum(['phase', 'task']),
        targetId: z.string().min(1),
      }))
      .query(async ({ ctx, input }) => {
        const chatRepo = requireChatSessionRepository(ctx);
        const session = await chatRepo.findActiveSession(input.targetType, input.targetId);
        if (!session) return null;
        const messages = await chatRepo.findMessagesBySessionId(session.id);
        return { ...session, messages };
      }),

    closeChatSession: publicProcedure
      .input(z.object({ sessionId: z.string().min(1) }))
      .mutation(async ({ ctx, input }) => {
        const chatRepo = requireChatSessionRepository(ctx);
        const agentManager = requireAgentManager(ctx);

        const session = await chatRepo.findSessionById(input.sessionId);
        if (!session) {
          throw new TRPCError({ code: 'NOT_FOUND', message: `Chat session '${input.sessionId}' not found` });
        }

        // Stop and dismiss agent if active
        if (session.agentId) {
          const agent = await agentManager.get(session.agentId);
          if (agent && !agent.userDismissedAt) {
            if (['running', 'waiting_for_input'].includes(agent.status)) {
              await agentManager.stop(agent.id);
            }
            await agentManager.dismiss(agent.id);
          }
        }

        await chatRepo.updateSession(input.sessionId, { status: 'closed' });

        ctx.eventBus.emit({
          type: 'chat:session_closed' as const,
          timestamp: new Date(),
          payload: { chatSessionId: session.id, initiativeId: session.initiativeId },
        });

        return { success: true };
      }),
  };
}
