/**
 * Tenant AI settings, quota, usage & credits routes — system-ai.
 * Mounted at /api/ai/*. Auth required; mutating routes need settings:write permission.
 * Personality / extra features require Business tier or above.
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { createDb } from '@zync/db/queries'
import {
  getTenantSettings,
  upsertTenantSettings,
  getCreditPurchases,
  tenantUsageBreakdown,
  getTierQuotas,
  getModelPricing,
  getGlobalConfig,
} from '@zync/db/queries'
import { TenantTier } from '@zync/types'
import type { AppEnv } from '../types'
import { requirePermission, requireTier } from '../middleware/guards'
import { authMiddleware } from '../middleware/auth'
import { AI_USE_CASE_LABELS, getQuotaStatus, usdToTokens } from '@zync/ai'
import type { SessionPayload } from '@zync/types'

const aiRoutes = new Hono<AppEnv>()

// All AI routes require auth
aiRoutes.use('*', authMiddleware)

// ── Tenant settings ───────────────────────────────────────────────────────────

aiRoutes.get('/settings', async (c) => {
  const session = c.get('session') as SessionPayload
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = createDb(c.env)
  const [settings, allQuotas] = await Promise.all([
    getTenantSettings(db, session.tid),
    getTierQuotas(db),
  ])

  const tierQuota = allQuotas.find((q) => q.tier === session.tier)

  return c.json({
    ...settings,
    tierLimits: {
      extraAllowed: tierQuota?.extraAllowed ?? false,
      extraMaxUsd: tierQuota?.extraMaxUsd ?? null,
    },
  })
})

const PutSettingsSchema = z.object({
  personalityPrompt: z.string().max(500).optional(),
  useCaseOverrides: z.record(z.string().max(300)).optional(),
  extraUsageEnabled: z.boolean().optional(),
  extraSpendLimitUsd: z.number().positive().optional(),
  autoReloadEnabled: z.boolean().optional(),
  autoReloadAmountUsd: z.number().min(5).max(100).optional(),
})

aiRoutes.put(
  '/settings',
  requireTier(TenantTier.BUSINESS),
  requirePermission('settings:write'),
  async (c) => {
    const session = c.get('session') as SessionPayload
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

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

    const db = createDb(c.env)

    // Cap extraSpendLimitUsd at tier max
    const allQuotas = await getTierQuotas(db)
    const tierQuota = allQuotas.find((q) => q.tier === session.tier)
    const tierMaxUsd = tierQuota?.extraMaxUsd ? parseFloat(tierQuota.extraMaxUsd) : null

    let extraSpendLimitUsd: string | null | undefined = undefined
    if (parsed.data.extraSpendLimitUsd !== undefined) {

      let capped = parsed.data.extraSpendLimitUsd
      if (tierMaxUsd !== null) capped = Math.min(capped, tierMaxUsd)
      extraSpendLimitUsd = capped.toFixed(2)
    }

    let autoReloadAmountUsd: string | null | undefined = undefined
    if (parsed.data.autoReloadAmountUsd !== undefined) {
      let capped = parsed.data.autoReloadAmountUsd
      if (tierMaxUsd !== null) capped = Math.min(capped, tierMaxUsd)
      autoReloadAmountUsd = capped.toFixed(2)
    }

    await upsertTenantSettings(db, session.tid, {
      ...('personalityPrompt' in parsed.data ? { personalityPrompt: parsed.data.personalityPrompt } : {}),
      ...('useCaseOverrides' in parsed.data ? { useCaseOverrides: parsed.data.useCaseOverrides } : {}),
      ...('extraUsageEnabled' in parsed.data ? { extraUsageEnabled: parsed.data.extraUsageEnabled } : {}),
      ...(extraSpendLimitUsd !== undefined ? { extraSpendLimitUsd } : {}),
      ...('autoReloadEnabled' in parsed.data ? { autoReloadEnabled: parsed.data.autoReloadEnabled } : {}),
      ...(autoReloadAmountUsd !== undefined ? { autoReloadAmountUsd } : {}),
    })

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

// ── Quota status ──────────────────────────────────────────────────────────────

aiRoutes.get('/quota', async (c) => {
  const session = c.get('session') as SessionPayload
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = createDb(c.env)
  const status = await getQuotaStatus(db, session.tid, session.tier)

  // Return only percentage data — never raw token counts per spec
  return c.json({
    percentUsed: status.percentUsed,
    tokensUsed: status.tokensUsed,
    tokensTotal: status.tokensTotal,
    resetsAt: status.resetsAt,
  })
})

// ── Usage analytics ───────────────────────────────────────────────────────────

const UsageRangeSchema = z.object({
  range: z.enum(['month', 'last3', 'last6']).optional().default('month'),
})

aiRoutes.get('/usage', async (c) => {
  const session = c.get('session') as SessionPayload
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const qParsed = UsageRangeSchema.safeParse(c.req.query())
  if (!qParsed.success) {
    return c.json({ error: 'Invalid query params', details: qParsed.error.flatten() }, 400)
  }
  const rangeParam = qParsed.data.range
  const now = new Date()
  let fromDate: Date

  if (rangeParam === 'last6') {
    fromDate = new Date(now.getFullYear(), now.getMonth() - 5, 1)
  } else if (rangeParam === 'last3') {
    fromDate = new Date(now.getFullYear(), now.getMonth() - 2, 1)
  } else {
    // current month
    fromDate = new Date(now.getFullYear(), now.getMonth(), 1)
  }

  const db = createDb(c.env)
  const breakdown = await tenantUsageBreakdown(db, session.tid, {
    from: fromDate,
    to: now,
  })

  return c.json({
    breakdown: breakdown.map((row) => ({
      ...row,
      label: AI_USE_CASE_LABELS[row.useCase as keyof typeof AI_USE_CASE_LABELS] ?? row.useCase,
    })),
  })
})

// ── Credits ───────────────────────────────────────────────────────────────────

aiRoutes.get('/credits', requireTier(TenantTier.BUSINESS), async (c) => {
  const session = c.get('session') as SessionPayload
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = createDb(c.env)
  const [purchases, config] = await Promise.all([
    getCreditPurchases(db, session.tid),
    getGlobalConfig(db),
  ])

  const mainPricing = await getModelPricing(db, config.mainModel.model)

  // Convert tokens_remaining to USD at current rate for display
  const purchasesWithUsd = purchases.map((p) => {
    let remainingUsd: number | null = null
    if (mainPricing) {
      const blendedRate = (mainPricing.inputCostPer1m + mainPricing.outputCostPer1m) / 2
      remainingUsd = (p.tokensRemaining / 1_000_000) * blendedRate
    }
    return {
      ...p,
      remainingUsd: remainingUsd !== null ? parseFloat(remainingUsd.toFixed(2)) : null,
    }
  })

  const totalRemainingUsd = purchasesWithUsd.reduce(
    (sum, p) => sum + (p.remainingUsd ?? 0),
    0,
  )

  return c.json({
    totalRemainingUsd: parseFloat(totalRemainingUsd.toFixed(2)),
    purchases: purchasesWithUsd,
  })
})

const PurchaseSchema = z.object({
  amountUsd: z.number().min(5).max(100),
})

aiRoutes.post(
  '/credits/purchase',
  requireTier(TenantTier.BUSINESS),
  requirePermission('settings:write'),
  async (c) => {
    const session = c.get('session') as SessionPayload
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const parsed = PurchaseSchema.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)

    // Convert USD to tokens at current main model rate (lock-in at purchase time)
    const tokensGranted = await usdToTokens(db, parsed.data.amountUsd, config.mainModel.model)

    // Return a payment intent for the payment adapter to process.
    // NOTE: The purchase record is NOT created here — tokens are only granted after
    // the payment gateway confirms payment via webhook. The payment spec owns that
    // webhook handler and calls insertCreditPurchase upon confirmed payment.
    // This endpoint returns the intent shape so the client can redirect to checkout.
    const paymentIntentId = `pi_ai_${Date.now()}_${session.tid.slice(0, 8)}`

    return c.json({
      paymentIntent: {
        id: paymentIntentId,
        amountUsd: parsed.data.amountUsd,
        tokensGranted,
        // Client must complete payment; tokens are not available until webhook confirms
        status: 'pending_payment',
      },
    }, 201)
  },
)

export { aiRoutes }
