/**
 * Secretaria — Tool Definitions
 *
 * Cada ToolDef é uma função que a IA pode chamar durante o atendimento.
 * O motor executa a função, injeta o resultado e chama a IA novamente.
 *
 * Tools disponíveis:
 *   listar_horarios    — agenda: horários livres
 *   agendar_horario    — agenda: reserva um slot
 *   escalar_humano     — handoff: transfere para atendente
 *   encerrar_protocolo — fecha o protocolo com resumo
 */
import { Knex } from 'knex'
import type { HookBus } from '../../backend/src/core/hook-bus'

// ── Tipos públicos ─────────────────────────────────────────────────────────────

export interface ToolParam {
  type: string
  description: string
  enum?: string[]
}

export interface ToolDef {
  name: string
  description: string
  parameters: {
    type: 'object'
    properties: Record<string, ToolParam>
    required?: string[]
  }
  execute: (args: Record<string, any>, ctx: ToolContext) => Promise<any>
}

export interface ToolContext {
  db: Knex
  conversationId: string
  extChatId?: string
  tenantId?: string
  hooks?: HookBus
  /** Telemetria cumulativa preenchida pelos tool loops (M1.4). */
  _telemetry?: {
    usage:    { input: number; output: number; total: number; cache_read?: number; cached?: number }
    provider: string
    model:    string
    iterations: number
  }
}

// ── Helpers ────────────────────────────────────────────────────────────────────

function fmtDate(d: Date): string {
  return d.toISOString().split('T')[0]!
}

// ── Tools ──────────────────────────────────────────────────────────────────────

export const BUILTIN_TOOLS: ToolDef[] = [

  // ── listar_horarios ──────────────────────────────────────────────────────────
  {
    name: 'listar_horarios',
    description:
      'Lista os horários disponíveis na agenda para agendamento. ' +
      'Use antes de propor datas ao cliente. ' +
      'Se não informar a data, retorna os próximos 7 dias.',
    parameters: {
      type: 'object',
      properties: {
        data: {
          type: 'string',
          description: 'Data no formato YYYY-MM-DD. Opcional — padrão: próximos 7 dias.',
        },
      },
    },
    async execute(args, ctx) {
      const from = args['data'] ?? fmtDate(new Date())
      const to   = args['data'] ?? fmtDate(new Date(Date.now() + 7 * 86_400_000))
      const slots = await ctx.db('sec_calendar')
        .where('status', 'available')
        .whereBetween('date', [from, to])
        .orderBy('date').orderBy('time_start')
        .limit(20)
      if (!slots.length) {
        return { disponivel: false, mensagem: 'Nenhum horário disponível no período informado.' }
      }
      return {
        disponivel: true,
        horarios: (slots as any[]).map(s => ({
          id:     s.id,
          data:   s.date,
          inicio: String(s.time_start).slice(0, 5),
          fim:    String(s.time_end).slice(0, 5),
          titulo: s.title,
        })),
      }
    },
  },

  // ── agendar_horario ──────────────────────────────────────────────────────────
  {
    name: 'agendar_horario',
    description:
      'Reserva um horário disponível na agenda para o cliente. ' +
      'Use listar_horarios primeiro para obter o slot_id correto.',
    parameters: {
      type: 'object',
      properties: {
        slot_id:          { type: 'string', description: 'ID do horário retornado por listar_horarios.' },
        nome_cliente:     { type: 'string', description: 'Nome completo do cliente.' },
        telefone_cliente: { type: 'string', description: 'Telefone do cliente (opcional).' },
      },
      required: ['slot_id', 'nome_cliente'],
    },
    async execute(args, ctx) {
      const slot = await ctx.db('sec_calendar')
        .where({ id: args['slot_id'], status: 'available' })
        .first()
      if (!slot) {
        return { ok: false, erro: 'Horário não encontrado ou já reservado. Chame listar_horarios novamente.' }
      }
      await ctx.db('sec_calendar').where({ id: args['slot_id'] }).update({
        status:        'booked',
        attendee_name:  args['nome_cliente'],
        attendee_phone: args['telefone_cliente'] ?? null,
        updated_at:     new Date(),
      })
      return {
        ok: true,
        confirmacao: {
          data:   slot.date,
          inicio: String(slot.time_start).slice(0, 5),
          fim:    String(slot.time_end).slice(0, 5),
          titulo: slot.title,
          nome:   args['nome_cliente'],
        },
      }
    },
  },

  // ── escalar_humano ───────────────────────────────────────────────────────────
  {
    name: 'escalar_humano',
    description:
      'Transfere o atendimento para um atendente humano quando a situação exige. ' +
      'Use quando: cliente pede explicitamente, problema financeiro sensível, raiva intensa, ' +
      'ou quando você não consegue resolver após tentativas honestas.',
    parameters: {
      type: 'object',
      properties: {
        motivo: {
          type: 'string',
          description: 'Motivo da transferência (ex: "cliente insatisfeito com cobrança").',
        },
      },
    },
    async execute(args, ctx) {
      await ctx.db('sec_conversations').where({ id: ctx.conversationId }).update({
        handoff_mode:     'humano',
        handoff_human_at: new Date(),
        status:           'escalated',
      })
      const conv = await ctx.db('sec_conversations').where({ id: ctx.conversationId }).first()
      if (ctx.hooks && ctx.tenantId && ctx.extChatId) {
        const chatId = ctx.extChatId.includes(':')
          ? ctx.extChatId.split(':').slice(1).join(':')
          : ctx.extChatId
        const payload = {
          tenantId:       ctx.tenantId,
          conversationId: ctx.conversationId,
          chatId,
          protocolNumber: conv?.protocol_number ?? '',
          motivo:         args['motivo'] ?? '',
        }
        ctx.hooks.emit('ext:handoff', { ...payload, mode: 'humano', reason: 'escalation' }).catch(() => {})
        ctx.hooks.emit('ext:escalated', payload).catch(() => {})
      }
      return { ok: true, escalado: true, motivo: args['motivo'] ?? 'Solicitado pelo sistema' }
    },
  },

  // ── encerrar_protocolo ───────────────────────────────────────────────────────
  {
    name: 'encerrar_protocolo',
    description:
      'Encerra o protocolo de atendimento após resolver o problema do cliente. ' +
      'Use apenas quando tiver certeza de que tudo foi resolvido.',
    parameters: {
      type: 'object',
      properties: {
        resumo: {
          type: 'string',
          description: 'Breve resumo do que foi tratado e resolvido neste atendimento.',
        },
      },
      required: ['resumo'],
    },
    async execute(args, ctx) {
      const summary = args['resumo'] ?? 'Atendimento encerrado.'
      const conv = await ctx.db('sec_conversations').where({ id: ctx.conversationId }).first()
      await ctx.db('sec_conversations').where({ id: ctx.conversationId }).update({
        status:     'closed',
        summary,
        updated_at: new Date(),
      })
      await ctx.db('sec_messages').where({ conversation_id: ctx.conversationId }).delete()
      return { ok: true, protocolo: conv?.protocol_number ?? '', resumo: summary }
    },
  },
]

export function resolveTools(names: string[]): ToolDef[] {
  return names
    .map(n => BUILTIN_TOOLS.find(t => t.name === n))
    .filter((t): t is ToolDef => t !== undefined)
}

export const ALL_TOOL_NAMES = BUILTIN_TOOLS.map(t => t.name)
