/**
 * Admin incident management API — system-status-page.
 *
 * POST /api/admin/incidents           — create a new incident
 * POST /api/admin/incidents/:id/update — post a status update
 * POST /api/admin/incidents/:id/resolve — resolve the incident
 *
 * All routes require a verified admin session. The authenticated admin's email
 * is stored as `created_by` on incident and update rows.
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { createDb } from '@zync/db/queries'
import {
  createIncident,
  addIncidentUpdate,
  resolveIncident,
  IncidentNotFoundError,
  IncidentAlreadyResolvedError,
} from '@zync/db/queries'
import { findAdminById } from '@zync/db/queries'
import type { AppEnv } from '../../types'
import { adminAuthMiddleware } from '../../middleware/admin-auth'
import { requireAdminSession } from '../../middleware/guards'
import { invalidateStatusCache } from '../../lib/status-cache'
import { notifySubscribers } from '../../lib/status-notify'
import type { AdminSessionPayload } from '@zync/types'
import { STATUS_SERVICES } from '@zync/types'
import type { StatusServiceId } from '@zync/types'

const adminIncidentsRoutes = new Hono<AppEnv>()

adminIncidentsRoutes.use('*', adminAuthMiddleware)
adminIncidentsRoutes.use('*', requireAdminSession())

// ── Schema ────────────────────────────────────────────────────────────────────

const SERVICE_IDS = STATUS_SERVICES.map((s) => s.id) as [StatusServiceId, ...StatusServiceId[]]

const CreateIncidentSchema = z.object({
  title: z.string().min(1),
  status: z.enum(['investigating', 'identified', 'monitoring', 'resolved']),
  impact: z.enum(['minor', 'major', 'critical']),
  affectedServices: z.array(z.enum(SERVICE_IDS)),
  body: z.string().min(1),
})

const PostUpdateSchema = z.object({
  status: z.enum(['investigating', 'identified', 'monitoring', 'resolved']),
  body: z.string().min(1),
})

const ResolveSchema = z.object({
  body: z.string().min(1),
})

const UuidParam = z.string().uuid()
type AdminSessionWithRole = AdminSessionPayload & { role?: string | null }

// ── Helper ────────────────────────────────────────────────────────────────────

async function resolveAdminEmail(db: ReturnType<typeof createDb>, adminId: string): Promise<string> {
  const admin = await findAdminById(db, adminId)
  if (!admin) throw new Error(`Admin not found: ${adminId}`)
  return admin.email
}

function requireSuperAdmin(c: { get: (key: 'session') => unknown }) {
  const session = c.get('session') as AdminSessionWithRole | undefined
  return session?.role === 'SUPER_ADMIN'
}

// ── POST /api/admin/incidents ─────────────────────────────────────────────────

adminIncidentsRoutes.post('/', async (c) => {
  if (!requireSuperAdmin(c)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  const parsed = CreateIncidentSchema.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)
  const createdBy = await resolveAdminEmail(db, session.sub)

  const incident = await createIncident(db, {
    title: parsed.data.title,
    status: parsed.data.status,
    impact: parsed.data.impact,
    affectedServices: parsed.data.affectedServices as StatusServiceId[],
    body: parsed.data.body,
    createdBy,
  })

  await invalidateStatusCache(c.env)
  // Fan-out email notifications (fire-and-forget; errors are logged inside)
  c.executionCtx.waitUntil(notifySubscribers(c.env, 'created', incident))

  return c.json(incident, 201)
})

// ── POST /api/admin/incidents/:id/update ──────────────────────────────────────

adminIncidentsRoutes.post('/:id/update', async (c) => {
  if (!requireSuperAdmin(c)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  const idResult = UuidParam.safeParse(c.req.param('id'))
  if (!idResult.success) {
    return c.json({ error: 'Invalid incident id' }, 400)
  }

  const parsed = PostUpdateSchema.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)
  const createdBy = await resolveAdminEmail(db, session.sub)

  try {
    const incident = await addIncidentUpdate(db, idResult.data, {
      body: parsed.data.body,
      status: parsed.data.status,
      createdBy,
    })

    await invalidateStatusCache(c.env)
    c.executionCtx.waitUntil(notifySubscribers(c.env, 'updated', incident))

    return c.json(incident)
  } catch (err) {
    if (err instanceof IncidentNotFoundError) {
      return c.json({ error: 'Incident not found' }, 404)
    }
    throw err
  }
})

// ── POST /api/admin/incidents/:id/resolve ─────────────────────────────────────

adminIncidentsRoutes.post('/:id/resolve', async (c) => {
  if (!requireSuperAdmin(c)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  const idResult = UuidParam.safeParse(c.req.param('id'))
  if (!idResult.success) {
    return c.json({ error: 'Invalid incident id' }, 400)
  }

  const parsed = ResolveSchema.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)
  const createdBy = await resolveAdminEmail(db, session.sub)

  try {
    const incident = await resolveIncident(db, idResult.data, {
      body: parsed.data.body,
      createdBy,
    })

    await invalidateStatusCache(c.env)
    c.executionCtx.waitUntil(notifySubscribers(c.env, 'resolved', incident))

    return c.json(incident)
  } catch (err) {
    if (err instanceof IncidentNotFoundError) {
      return c.json({ error: 'Incident not found' }, 404)
    }
    if (err instanceof IncidentAlreadyResolvedError) {
      return c.json({ error: 'Incident is already resolved' }, 409)
    }
    throw err
  }
})

export { adminIncidentsRoutes }
