/**
 * Tool executors. Серверная сторона вызывает tools через loopback на
 * /api/voice/tools/* и /api/voice/timer (тот же Next.js process).
 * Bearer = VOICE_API_KEY, плюс x-voice-internal для middleware bypass.
 *
 * Порт satellite/tools.py.
 */

const baseUrl = () => `http://localhost:${process.env.PORT || '3000'}`

function headers(): Record<string, string> {
  const key = process.env.VOICE_API_KEY || ''
  return {
    Authorization: `Bearer ${key}`,
    'x-voice-internal': key,
    'Content-Type': 'application/json',
  }
}

async function tabletGet(path: string, params?: Record<string, string>): Promise<any> {
  const url = new URL(`${baseUrl()}${path}`)
  if (params) {
    for (const [k, v] of Object.entries(params)) {
      if (v !== undefined && v !== '') url.searchParams.set(k, v)
    }
  }
  const r = await fetch(url, { headers: headers(), cache: 'no-store' })
  if (!r.ok) throw new ToolHttpError(r.status, await r.text().catch(() => ''))
  return r.json()
}

async function tabletJson(method: 'POST' | 'PUT' | 'DELETE', path: string, body?: any, params?: Record<string, string>): Promise<any> {
  const url = new URL(`${baseUrl()}${path}`)
  if (params) {
    for (const [k, v] of Object.entries(params)) {
      if (v !== undefined && v !== '') url.searchParams.set(k, v)
    }
  }
  const r = await fetch(url, {
    method,
    headers: headers(),
    body: body !== undefined ? JSON.stringify(body) : undefined,
  })
  if (!r.ok) throw new ToolHttpError(r.status, await r.text().catch(() => ''))
  return r.json()
}

class ToolHttpError extends Error {
  constructor(public status: number, public body: string) {
    super(`tool_http_${status}`)
  }
}

type AgentId = 'cosmo' | 'lusya'
type ToolResult = Record<string, any> | { error: string }

const EXECUTORS: Record<string, (params: any, agent: AgentId) => Promise<ToolResult>> = {
  async get_weather(params) {
    const city = (params?.city as string) || ''
    return tabletGet('/api/voice/tools/weather', city ? { city } : undefined)
  },

  async get_transport(params) {
    const q: Record<string, string> = {}
    if (params?.direction) q.direction = String(params.direction)
    if (params?.routes) q.routes = String(params.routes)
    return tabletGet('/api/voice/tools/transport', q)
  },

  async get_today_events(params) {
    const range = (params?.range as string) || 'today'
    return tabletGet('/api/voice/tools/events', { range })
  },

  async create_event(params) {
    const title = String(params?.title || '').trim()
    const date = String(params?.date || '').trim()
    if (!title || !date) return { error: 'title and date required' }
    const payload: Record<string, any> = {
      title,
      date,
      owner: params?.owner || 'daniil',
      all_day: !!params?.all_day,
    }
    if (!payload.all_day) {
      payload.start_time = params?.start_time || ''
      if (params?.end_time !== undefined) payload.end_time = params.end_time
    }
    return tabletJson('POST', '/api/voice/tools/events', payload)
  },

  async update_event(params) {
    const event_id = String(params?.event_id || '').trim()
    const owner = String(params?.owner || '').trim()
    if (!event_id || !owner) return { error: 'event_id and owner required' }
    const payload: Record<string, any> = { event_id, owner }
    for (const k of ['title', 'date', 'start_time', 'end_time', 'all_day']) {
      if (params?.[k] !== undefined) payload[k] = params[k]
    }
    return tabletJson('PUT', '/api/voice/tools/events', payload)
  },

  async delete_event(params) {
    const event_id = String(params?.event_id || '').trim()
    const owner = String(params?.owner || 'daniil').trim()
    if (!event_id) return { error: 'event_id required' }
    return tabletJson('DELETE', '/api/voice/tools/events', undefined, { event_id, owner })
  },

  async get_notes() {
    return tabletGet('/api/voice/tools/notes')
  },

  async set_timer(params, agent) {
    const seconds = Number(params?.seconds || 0)
    const label = String(params?.label || 'Таймер')
    if (!Number.isFinite(seconds) || seconds < 1) return { error: 'seconds must be positive' }
    return tabletJson('POST', '/api/voice/timer', {
      action: 'start',
      seconds,
      label,
      agent,
    })
  },

  async cancel_timer(params) {
    const label = String(params?.label || '').trim()
    if (!label) return { error: 'label required' }
    return tabletJson('POST', '/api/voice/timer', { action: 'cancel', label })
  },

  async adjust_timer(params) {
    const label = String(params?.label || '').trim()
    const delta = Number(params?.delta_seconds || 0)
    if (!label) return { error: 'label required' }
    if (!Number.isFinite(delta) || delta === 0) return { error: 'delta_seconds must be non-zero' }
    return tabletJson('POST', '/api/voice/timer', {
      action: 'adjust',
      label,
      delta_seconds: delta,
    })
  },
}

export async function executeTool(name: string, params: any, agent: AgentId): Promise<ToolResult> {
  const fn = EXECUTORS[name]
  if (!fn) return { error: `unknown tool: ${name}` }
  try {
    return await fn(params || {}, agent)
  } catch (e) {
    if (e instanceof ToolHttpError) {
      return { error: `tool_http_${e.status}` }
    }
    if (e instanceof TypeError && /fetch/i.test(e.message)) {
      return { error: 'tool_network_error' }
    }
    return { error: `tool_exception: ${(e as Error).message}` }
  }
}
