import type { FastifyInstance } from 'fastify'
import { z } from 'zod'
import { db, logEvent } from '../db/index.js'
import { encryptSecret } from '../db/crypto.js'
import { loadSecrets } from '../db/secrets.js'
import { adapterRegistry } from '../integrations/registry.js'
import type { IntegrationType, Resource } from '../integrations/types.js'

const integrationTypes = [
  'proxmox',
  'docker',
  'netbird',
  'cloudflare',
  'aws',
  'uptime_kuma',
  'weather',
  'ssh',
  'remote_desktop',
] as const

const createSchema = z.object({
  type: z.enum(integrationTypes),
  name: z.string().min(1).max(128),
  config: z.record(z.string(), z.string()).default({}),
  secrets: z.record(z.string(), z.string()).default({}),
})

interface IntegrationRow {
  id: number
  type: string
  name: string
  enabled: number
  status: string
  config_json: string
  last_checked_at: string | null
  created_at: string
}

function serialize(row: IntegrationRow) {
  return {
    id: row.id,
    type: row.type,
    name: row.name,
    enabled: !!row.enabled,
    status: row.status,
    config: JSON.parse(row.config_json),
    lastCheckedAt: row.last_checked_at,
    createdAt: row.created_at,
  }
}

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

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

  app.post('/api/integrations', 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 { type, name, config, secrets } = parsed.data
    const result = db
      .prepare('INSERT INTO integrations (type, name, config_json) VALUES (?, ?, ?)')
      .run(type, name, JSON.stringify(config))
    const integrationId = Number(result.lastInsertRowid)
    const insertSecret = db.prepare(
      'INSERT INTO secrets (integration_id, key, value_encrypted) VALUES (?, ?, ?)'
    )
    for (const [key, value] of Object.entries(secrets)) {
      insertSecret.run(integrationId, key, encryptSecret(value))
    }
    const row = db.prepare('SELECT * FROM integrations WHERE id = ?').get(integrationId) as IntegrationRow
    logEvent('integration_created', `${name} integration added`, type)
    return reply.code(201).send({ integration: serialize(row) })
  })

  app.put('/api/integrations/:id', async (req, reply) => {
    const id = Number((req.params as { id: string }).id)
    const parsed = createSchema.partial().safeParse(req.body)
    if (!parsed.success) {
      return reply.code(400).send({ error: parsed.error.issues[0]?.message ?? 'Invalid input' })
    }
    const existing = db.prepare('SELECT * FROM integrations WHERE id = ?').get(id) as
      | IntegrationRow
      | undefined
    if (!existing) return reply.code(404).send({ error: 'Not found' })

    const name = parsed.data.name ?? existing.name
    const config = parsed.data.config ?? JSON.parse(existing.config_json)
    db.prepare('UPDATE integrations SET name = ?, config_json = ? WHERE id = ?').run(
      name,
      JSON.stringify(config),
      id
    )
    if (parsed.data.secrets) {
      const upsert = db.prepare(
        `INSERT INTO secrets (integration_id, key, value_encrypted) VALUES (?, ?, ?)
         ON CONFLICT(integration_id, key) DO UPDATE SET value_encrypted = excluded.value_encrypted`
      )
      for (const [key, value] of Object.entries(parsed.data.secrets)) {
        upsert.run(id, key, encryptSecret(value))
      }
    }
    const row = db.prepare('SELECT * FROM integrations WHERE id = ?').get(id) as IntegrationRow
    return { integration: serialize(row) }
  })

  app.delete('/api/integrations/:id', async (req, reply) => {
    const id = Number((req.params as { id: string }).id)
    const existing = db.prepare('SELECT * FROM integrations WHERE id = ?').get(id) as IntegrationRow | undefined
    db.prepare('DELETE FROM integrations WHERE id = ?').run(id)
    if (existing) logEvent('integration_deleted', `${existing.name} integration removed`, existing.type)
    return reply.code(204).send()
  })

  app.post('/api/integrations/:id/test', async (req, reply) => {
    const id = Number((req.params as { id: string }).id)
    const row = db.prepare('SELECT * FROM integrations WHERE id = ?').get(id) as
      | IntegrationRow
      | undefined
    if (!row) return reply.code(404).send({ error: 'Not found' })

    const adapter = adapterRegistry[row.type as IntegrationType]
    const config = JSON.parse(row.config_json)
    const secrets = loadSecrets(id)
    const result = await adapter.testConnection(config, secrets)
    const status = result.ok ? 'connected' : 'error'
    db.prepare("UPDATE integrations SET status = ?, last_checked_at = datetime('now') WHERE id = ?").run(
      status,
      id
    )
    logEvent('integration_tested', `${row.name} test ${result.ok ? 'succeeded' : 'failed'}`, row.type)
    return result
  })

  app.get('/api/integrations/resources', async () => {
    const rows = db.prepare("SELECT * FROM integrations WHERE enabled = 1 AND status = 'connected'").all() as IntegrationRow[]
    const resources: (Resource & { integration: string })[] = []
    for (const row of rows) {
      const adapter = adapterRegistry[row.type as IntegrationType]
      if (!adapter.listResources) continue
      const config = JSON.parse(row.config_json)
      const secrets = loadSecrets(row.id)
      try {
        const found = await adapter.listResources(config, secrets)
        for (const r of found) resources.push({ ...r, integration: row.name })
      } catch {
        // adapter unreachable — skip, connection test already surfaces this
      }
    }
    return { resources }
  })
}
