/**
 * Admin AI configuration & analytics routes — system-ai.
 * Mounted at /api/admin/ai/*. All routes behind requireAdminSession().
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { createDb } from '@zync/db/queries'
import {
  getGlobalConfig,
  updateGlobalConfig,
  listModelPricing,
  addModel,
  updateModelPricing,
  setModelActive,
  getTierQuotas,
  upsertTierQuota,
  listUsageLog,
  tokensByUseCase,
  costByTenant,
  errorRateByModel,
  costTrend,
} from '@zync/db/queries'
import { executeWithFallback, type AISettings } from '@zync/ai'
import type { AppEnv } from '../../types'
import { requireAdminSession } from '../../middleware/guards'
import { adminAuthMiddleware } from '../../middleware/admin-auth'
import type { AdminSessionPayload } from '@zync/types'

const adminAiRoutes = new Hono<AppEnv>()

// Apply auth middleware and admin session guard to all routes
adminAiRoutes.use('*', adminAuthMiddleware)
adminAiRoutes.use('*', requireAdminSession())

// ── Global config ─────────────────────────────────────────────────────────────

adminAiRoutes.get('/config', async (c) => {
  const db = createDb(c.env)
  const config = await getGlobalConfig(db)
  return c.json(config)
})

const UpdateConfigSchema = z.object({
  mainModelId: z.string().min(1),
  backupModelIds: z.array(z.string()),
  useCasePrompts: z.record(z.string()),
})

adminAiRoutes.put('/config', async (c) => {
  const parsed = UpdateConfigSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  }

  const session = c.get('session') as AdminSessionPayload
  const db = createDb(c.env)

  await updateGlobalConfig(db, {
    mainModelId: parsed.data.mainModelId,
    backupModelIds: parsed.data.backupModelIds,
    useCasePrompts: parsed.data.useCasePrompts,
    updatedBy: session.sub,
  })

  return c.json({ success: true })
})

// ── Models ────────────────────────────────────────────────────────────────────

adminAiRoutes.get('/models', async (c) => {
  const activeOnly = c.req.query('active') === 'true'
  const db = createDb(c.env)
  const models = await listModelPricing(db, { activeOnly })
  return c.json(models)
})

const AddModelSchema = z.object({
  modelId: z.string().min(1),
  provider: z.enum(['anthropic', 'openai', 'google']),
  label: z.string().min(1),
  inputCostPer1m: z.string().regex(/^\d+(\.\d+)?$/),
  outputCostPer1m: z.string().regex(/^\d+(\.\d+)?$/),
  isVisionCapable: z.boolean().optional().default(false),
  active: z.boolean().optional().default(true),
})

adminAiRoutes.post('/models', async (c) => {
  const parsed = AddModelSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  await addModel(db, {
    modelId: parsed.data.modelId,
    provider: parsed.data.provider,
    label: parsed.data.label,
    inputCostPer1m: parsed.data.inputCostPer1m,
    outputCostPer1m: parsed.data.outputCostPer1m,
    isVisionCapable: parsed.data.isVisionCapable,
    active: parsed.data.active,
  })

  return c.json({ success: true }, 201)
})

const UpdateModelSchema = z.object({
  label: z.string().min(1).optional(),
  provider: z.enum(['anthropic', 'openai', 'google']).optional(),
  inputCostPer1m: z.string().regex(/^\d+(\.\d+)?$/).optional(),
  outputCostPer1m: z.string().regex(/^\d+(\.\d+)?$/).optional(),
  isVisionCapable: z.boolean().optional(),
})

adminAiRoutes.put('/models/:modelId', async (c) => {
  const modelId = c.req.param('modelId')
  const parsed = UpdateModelSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  await updateModelPricing(db, modelId, parsed.data)
  return c.json({ success: true })
})

const SetActiveSchema = z.object({
  active: z.boolean(),
})

adminAiRoutes.patch('/models/:modelId/active', async (c) => {
  const modelId = c.req.param('modelId')
  const parsed = SetActiveSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  await setModelActive(db, modelId, parsed.data.active)
  return c.json({ success: true })
})

// ── Tier quotas ───────────────────────────────────────────────────────────────

adminAiRoutes.get('/quotas', async (c) => {
  const db = createDb(c.env)
  const quotas = await getTierQuotas(db)
  return c.json(quotas)
})

const VALID_TIERS = ['freelancer', 'business', 'enterprise', 'white_label'] as const
const UpsertQuotaSchema = z.object({
  modelId: z.string().min(1),
  monthlyTokens: z.number().int().positive(),
  extraAllowed: z.boolean(),
  extraMaxUsd: z.string().regex(/^\d+(\.\d+)?$/).nullable().optional(),
})

adminAiRoutes.put('/quotas/:tier', async (c) => {
  const tier = c.req.param('tier')
  if (!VALID_TIERS.includes(tier as (typeof VALID_TIERS)[number])) {
    return c.json({ error: 'Invalid tier' }, 400)
  }

  const parsed = UpsertQuotaSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  await upsertTierQuota(db, tier, {
    modelId: parsed.data.modelId,
    monthlyTokens: parsed.data.monthlyTokens,
    extraAllowed: parsed.data.extraAllowed,
    extraMaxUsd: parsed.data.extraMaxUsd ?? null,
  })

  return c.json({ success: true })
})

// ── Usage log ─────────────────────────────────────────────────────────────────

const UsageQuerySchema = z.object({
  tenant: z.string().optional(),
  use_case: z.string().optional(),
  model: z.string().optional(),
  from: z.string().optional(),
  to: z.string().optional(),
  page: z.coerce.number().int().positive().optional().default(1),
  pageSize: z.coerce.number().int().positive().max(200).optional().default(50),
  format: z.enum(['json', 'csv']).optional().default('json'),
})

adminAiRoutes.get('/usage', async (c) => {
  const query = c.req.query()
  const parsed = UsageQuerySchema.safeParse(query)
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  const { rows, total } = await listUsageLog(
    db,
    {
      tenantId: parsed.data.tenant,
      useCase: parsed.data.use_case,
      modelId: parsed.data.model,
      from: parsed.data.from ? new Date(parsed.data.from) : undefined,
      to: parsed.data.to ? new Date(parsed.data.to) : undefined,
    },
    { page: parsed.data.page, pageSize: parsed.data.pageSize },
  )

  if (parsed.data.format === 'csv') {
    const headers = [
      'id', 'tenant_id', 'user_id', 'use_case', 'model_id', 'provider',
      'input_tokens', 'output_tokens', 'total_tokens', 'cost_usd',
      'billed_from', 'duration_ms', 'entity_type', 'entity_id', 'error', 'created_at',
    ]
    const csvRows = rows.map((r) => [
      r.id, r.tenantId, r.userId ?? '',
      r.useCase, r.modelId, r.provider,
      r.inputTokens, r.outputTokens, r.totalTokens, r.costUsd,
      r.billedFrom, r.durationMs,
      r.entityType ?? '', r.entityId ?? '',
      r.error ?? '',
      r.createdAt.toISOString(),
    ].map((v) => `"${String(v).replace(/"/g, '""')}"`).join(','))

    const csv = [headers.join(','), ...csvRows].join('\n')
    return new Response(csv, {
      headers: {
        'Content-Type': 'text/csv',
        'Content-Disposition': 'attachment; filename="ai-usage.csv"',
      },
    })
  }

  return c.json({ rows, total, page: parsed.data.page, pageSize: parsed.data.pageSize })
})

// ── Analytics ─────────────────────────────────────────────────────────────────

const AnalyticsQuerySchema = z.object({
  period: z.string().regex(/^\d{4}-\d{2}$/).optional(),
})

adminAiRoutes.get('/analytics', async (c) => {
  const query = c.req.query()
  const parsed = AnalyticsQuerySchema.safeParse(query)
  if (!parsed.success) {
    return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
  }

  const now = new Date()
  const defaultPeriod = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
  const period = parsed.data.period ?? defaultPeriod

  const db = createDb(c.env)
  const [byUseCase, byTenant, byModel, trend] = await Promise.all([
    tokensByUseCase(db, period),
    costByTenant(db, period),
    errorRateByModel(db, period),
    costTrend(db, 6),
  ])

  return c.json({
    period,
    tokensByUseCase: byUseCase,
    costByTenant: byTenant,
    errorRateByModel: byModel,
    costTrend: trend,
  })
})

// ── Prompt test ───────────────────────────────────────────────────────────────

const TestPromptSchema = z.object({
  useCase: z.string().min(1),
  systemPrompt: z.string().min(1),
  testMessage: z.string().optional().default('Hello, please respond to confirm this prompt works.'),
})

adminAiRoutes.post('/test-prompt', async (c) => {
  const parsed = TestPromptSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  }

  const db = createDb(c.env)
  const config = await getGlobalConfig(db)

  const settings: AISettings = {
    mainModel: config.mainModel,
    backupModels: config.backupModels,
  }

  try {
    const response = await executeWithFallback(
      {
        messages: [
          { role: 'system', content: parsed.data.systemPrompt },
          { role: 'user', content: parsed.data.testMessage },
        ],
        model: settings.mainModel.model,
        maxTokens: 256,
      },
      settings,
      c.env,
      { timeoutMs: 15_000 },
    )

    return c.json({
      response: response.content,
      model: response.model,
      provider: response.provider,
      inputTokens: response.inputTokens,
      outputTokens: response.outputTokens,
      durationMs: response.durationMs,
    })
  } catch (err) {
    return c.json(
      { error: err instanceof Error ? err.message : 'Test prompt failed' },
      502,
    )
  }
})

export { adminAiRoutes }
