import { prisma } from "./prisma.js";
import { createId } from "./id.js";
import { PASSPORT_NUMBER_AGENT_PROMPT } from "./passport-reader-agent.js";

export type SystemAgentCode =
  | "workspace_global_assistant"
  | "workspace_agent_router"
  | "document_generation_orchestrator"
  | "passport_number_reader";

export type SystemAgentDefinition = {
  agentCode: SystemAgentCode;
  name: string;
  description: string;
  defaultInstructions: string;
};

function buildDefaultWorkspaceGlobalAssistantPrompt(lawFirmName: string) {
  return [
    `Você é o assistente global do workspace ${lawFirmName}.`,
    "Responda sempre em português do Brasil.",
    "Você deve consolidar conhecimento de agentes, chats, revisões documentais, casos, clientes e documentos da base de conhecimento do workspace.",
    "Baseie sua resposta apenas no contexto do workspace fornecido e no histórico desta conversa.",
    "Quando a base fornecida for insuficiente, diga isso claramente em vez de inventar.",
    "Prefira respostas objetivas, práticas e orientadas à operação jurídica do workspace.",
    "Se houver sinais conflitantes entre agentes ou registros, explicite o conflito e a sua melhor leitura.",
  ].join(" ");
}

function buildDefaultWorkspaceAgentRouterPrompt(lawFirmName: string) {
  return [
    `Você é o roteador inteligente de agentes do workspace ${lawFirmName}.`,
    "Responda sempre em português do Brasil quando precisar justificar uma decisão internamente.",
    "Sua função é analisar a pergunta do usuário e escolher qual agente do banco deve responder.",
    "Você receberá uma lista de agentes disponíveis com tipo, contexto e descrição.",
    "Prefira custom agents para lidar com situações de clientes, casos, documentos e operações jurídicas genéricas.",
    "Use internal profiles apenas quando a pergunta depender explicitamente da memória, estilo ou histórico interno de uma pessoa do time.",
    "Use o agente global quando a pergunta for transversal ao workspace, ou quando nenhum agente especializado for claramente adequado.",
    "Nunca escolha um agente inexistente.",
    "Se houver dúvida real entre dois agentes, escolha o mais conservador e explique a razão.",
    "Responda apenas em JSON válido.",
  ].join(" ");
}

function buildDefaultDocumentGenerationOrchestratorPrompt(lawFirmName: string) {
  return [
    `Você é o orquestrador operacional de geração documental do workspace ${lawFirmName}.`,
    "Responda sempre em português do Brasil quando precisar justificar a decisão internamente.",
    "Sua função é escolher qual agente deve ser usado em cada etapa operacional de leitura de documento, extração estruturada, preenchimento de formulário ou montagem do processo.",
    "Você receberá o tipo da operação, o stageCode, um resumo do contexto e a lista de agentes disponíveis.",
    "Escolha apenas um agente compatível com a etapa.",
    "Prefira agents de formulário quando o passo for preencher um formulário específico e houver um agente vinculado ao template.",
    "Prefira passport_number_reader somente quando a etapa envolver leitura de número de passaporte ou confirmação de passaporte.",
    "Prefira custom agents apenas quando a descrição deles combinar claramente com o documento, formulário ou passo operacional.",
    "Use built-in generic agents quando não houver especialista claro.",
    "Não escolha um agente incompatível com a etapa só porque ele parece mais específico em abstrato.",
    "Se houver dúvida real entre um especialista e um genérico, escolha o mais conservador e explique a razão.",
    "Responda apenas em JSON válido.",
  ].join(" ");
}

export function getSystemAgentDefinitions(input: {
  lawFirmName: string;
}): SystemAgentDefinition[] {
  return [
    {
      agentCode: "workspace_global_assistant",
      name: "Workspace Global Assistant",
      description:
        "Consolida conhecimento do workspace inteiro para responder perguntas operacionais e jurídicas com base na memória institucional.",
      defaultInstructions: buildDefaultWorkspaceGlobalAssistantPrompt(input.lawFirmName),
    },
    {
      agentCode: "workspace_agent_router",
      name: "Workspace Agent Router",
      description:
        "Analisa os agentes disponíveis no banco e decide automaticamente qual deles deve responder cada pergunta.",
      defaultInstructions: buildDefaultWorkspaceAgentRouterPrompt(input.lawFirmName),
    },
    {
      agentCode: "document_generation_orchestrator",
      name: "Document Generation Orchestrator",
      description:
        "Escolhe o agente operacional mais adequado para cada etapa de leitura de documento, extração estruturada e preenchimento de formulário.",
      defaultInstructions: buildDefaultDocumentGenerationOrchestratorPrompt(input.lawFirmName),
    },
    {
      agentCode: "passport_number_reader",
      name: "Passport Number Reader",
      description:
        "Especialista em leitura de passaporte para identificar o número correto sem confundir com personal number, previous passport number ou MRZ.",
      defaultInstructions: PASSPORT_NUMBER_AGENT_PROMPT,
    },
  ];
}

let ensureSystemAgentInstructionsTablePromise: Promise<void> | null = null;

export async function ensureSystemAgentInstructionsTable() {
  if (!ensureSystemAgentInstructionsTablePromise) {
    ensureSystemAgentInstructionsTablePromise = (async () => {
      await prisma.$executeRawUnsafe(`
        CREATE TABLE IF NOT EXISTS law_firm_agent_instructions (
          id CHAR(36) NOT NULL PRIMARY KEY,
          law_firm_id CHAR(36) NOT NULL,
          agent_scope VARCHAR(50) NOT NULL,
          agent_key VARCHAR(120) NOT NULL,
          instructions_text LONGTEXT NOT NULL,
          created_by_user_id CHAR(36) NULL,
          updated_by_user_id CHAR(36) NULL,
          created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
          UNIQUE KEY uq_law_firm_agent_instructions (law_firm_id, agent_scope, agent_key),
          KEY idx_law_firm_agent_instructions_scope (law_firm_id, agent_scope),
          CONSTRAINT fk_law_firm_agent_instructions_law_firm
            FOREIGN KEY (law_firm_id) REFERENCES law_firms (id),
          CONSTRAINT fk_law_firm_agent_instructions_created_by
            FOREIGN KEY (created_by_user_id) REFERENCES users (id),
          CONSTRAINT fk_law_firm_agent_instructions_updated_by
            FOREIGN KEY (updated_by_user_id) REFERENCES users (id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
      `);
    })().catch((error) => {
      ensureSystemAgentInstructionsTablePromise = null;
      throw error;
    });
  }

  await ensureSystemAgentInstructionsTablePromise;
}

export async function listSystemAgentInstructions(input: { lawFirmId: string }) {
  await ensureSystemAgentInstructionsTable();

  return prisma.$queryRaw<
    Array<{
      agent_scope: string;
      agent_key: string;
      instructions_text: string;
      updated_at: Date;
    }>
  >`
    SELECT agent_scope, agent_key, instructions_text, updated_at
    FROM law_firm_agent_instructions
    WHERE law_firm_id = ${input.lawFirmId}
      AND agent_scope = 'system_agent'
  `;
}

export async function resolveSystemAgentInstructions(input: {
  lawFirmId: string;
  lawFirmName: string;
  agentCode: SystemAgentCode;
}) {
  const definition =
    getSystemAgentDefinitions({ lawFirmName: input.lawFirmName }).find(
      (item) => item.agentCode === input.agentCode,
    ) ?? null;

  if (!definition) {
    throw new Error(`Unknown system agent: ${input.agentCode}`);
  }

  const overrides = await listSystemAgentInstructions({
    lawFirmId: input.lawFirmId,
  });
  const override =
    overrides.find(
      (item) => item.agent_scope === "system_agent" && item.agent_key === input.agentCode,
    ) ?? null;

  return {
    ...definition,
    instructions: String(override?.instructions_text ?? "").trim() || definition.defaultInstructions,
    isCustomized: Boolean(String(override?.instructions_text ?? "").trim()),
    updatedAt: override?.updated_at ?? null,
  };
}

export async function upsertSystemAgentInstructions(input: {
  lawFirmId: string;
  actorUserId: string;
  agentCode: SystemAgentCode;
  instructions: string;
}) {
  await ensureSystemAgentInstructionsTable();

  const normalizedInstructions = String(input.instructions ?? "").trim();
  if (!normalizedInstructions) {
    throw new Error("Instructions cannot be empty");
  }

  const [existing] = await prisma.$queryRaw<Array<{ id: string }>>`
    SELECT id
    FROM law_firm_agent_instructions
    WHERE law_firm_id = ${input.lawFirmId}
      AND agent_scope = 'system_agent'
      AND agent_key = ${input.agentCode}
    LIMIT 1
  `;

  if (existing) {
    await prisma.$executeRaw`
      UPDATE law_firm_agent_instructions
      SET
        instructions_text = ${normalizedInstructions},
        updated_by_user_id = ${input.actorUserId},
        updated_at = CURRENT_TIMESTAMP
      WHERE id = ${existing.id}
    `;
    return existing.id;
  }

  const id = createId();
  await prisma.$executeRaw`
    INSERT INTO law_firm_agent_instructions (
      id, law_firm_id, agent_scope, agent_key, instructions_text,
      created_by_user_id, updated_by_user_id, created_at, updated_at
    ) VALUES (
      ${id},
      ${input.lawFirmId},
      'system_agent',
      ${input.agentCode},
      ${normalizedInstructions},
      ${input.actorUserId},
      ${input.actorUserId},
      CURRENT_TIMESTAMP,
      CURRENT_TIMESTAMP
    )
  `;

  return id;
}
