import type { FastifyInstance } from 'fastify'
import { z } from 'zod'
import { db, logEvent } from '../db/index.js'
import {
  getStatus,
  getTunnelRow,
  startTunnel,
  stopTunnel,
  deleteTunnelRuntime,
  type TunnelRow,
} from '../tunnels/manager.js'

const createSchema = z.object({
  name: z.string().min(1).max(128),
  integrationId: z.number().int(),
  mode: z.enum(['local', 'remote', 'dynamic']),
  sourcePort: z.number().int().min(1).max(65535),
  endpointHost: z.string().default(''),
  endpointPort: z.number().int().min(0).max(65535).default(0),
  autoStart: z.boolean().default(false),
  maxRetries: z.number().int().min(0).max(20).default(3),
  retryIntervalMs: z.number().int().min(500).max(60000).default(5000),
})

function serialize(row: TunnelRow) {
  return {
    id: row.id,
    name: row.name,
    integrationId: row.integration_id,
    mode: row.mode,
    sourcePort: row.source_port,
    endpointHost: row.endpoint_host,
    endpointPort: row.endpoint_port,
    autoStart: !!row.auto_start,
    maxRetries: row.max_retries,
    retryIntervalMs: row.retry_interval_ms,
    createdAt: row.created_at,
    ...getStatus(row.id),
  }
}

export async function tunnelRoutes(app: FastifyInstance) {
  app.addHook('onRequest', app.authenticate)

  app.get('/api/tunnels', async () => {
    const rows = db.prepare('SELECT * FROM tunnels ORDER BY created_at').all() as TunnelRow[]
    return { tunnels: rows.map(serialize) }
  })

  app.post('/api/tunnels', async (req, reply) => {
    const parsed = createSchema.safeParse(req.body)
    if (!parsed.success) {
      return reply.code(400).send({ error: parsed.error.issues[0]?.message ?? 'Invalid input' })
    }
    const d = parsed.data
    const result = db
      .prepare(
        `INSERT INTO tunnels (name, integration_id, mode, source_port, endpoint_host, endpoint_port, auto_start, max_retries, retry_interval_ms)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      )
      .run(d.name, d.integrationId, d.mode, d.sourcePort, d.endpointHost, d.endpointPort, d.autoStart ? 1 : 0, d.maxRetries, d.retryIntervalMs)
    const id = Number(result.lastInsertRowid)
    const row = getTunnelRow(id)!
    logEvent('tunnel_created', `${d.name} tunnel added`, d.mode)
    return reply.code(201).send({ tunnel: serialize(row) })
  })

  app.delete('/api/tunnels/:id', async (req, reply) => {
    const id = Number((req.params as { id: string }).id)
    const row = getTunnelRow(id)
    if (!row) return reply.code(404).send({ error: 'Tunnel not found' })
    deleteTunnelRuntime(id)
    db.prepare('DELETE FROM tunnels WHERE id = ?').run(id)
    logEvent('tunnel_deleted', `${row.name} tunnel removed`, row.mode)
    return { ok: true }
  })

  app.post('/api/tunnels/:id/connect', async (req, reply) => {
    const id = Number((req.params as { id: string }).id)
    const row = getTunnelRow(id)
    if (!row) return reply.code(404).send({ error: 'Tunnel not found' })
    startTunnel(id)
    return { ok: true, ...getStatus(id) }
  })

  app.post('/api/tunnels/:id/disconnect', async (req, reply) => {
    const id = Number((req.params as { id: string }).id)
    const row = getTunnelRow(id)
    if (!row) return reply.code(404).send({ error: 'Tunnel not found' })
    stopTunnel(id)
    return { ok: true, ...getStatus(id) }
  })
}
