/**
 * Custom analytics dashboard CRUD — reports-analytics (wave C).
 *
 * Mounted at /api/dashboards in routes/index.ts.
 *
 * GET    /                    → list user's dashboards (lazy-create default Overview)
 * POST   /                    → create dashboard (reports:write)
 * GET    /:id                 → dashboard + widgets (reports:read)
 * PATCH  /:id                 → rename / set default (reports:write)
 * DELETE /:id                 → delete dashboard + widgets (reports:write)
 * POST   /:id/widgets         → add widget (reports:write)
 * PATCH  /:id/widgets/:wid    → update widget (reports:write)
 * DELETE /:id/widgets/:wid      → remove widget (reports:write)
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'
import {
  ensureDefaultDashboard,
  createDashboard,
  getDashboardWithWidgets,
  updateDashboard,
  deleteDashboard,
  addDashboardWidget,
  updateDashboardWidget,
  deleteDashboardWidget,
  WIDGET_TYPES,
} from '@zync/db/queries'

const widgetTypeSchema = z.enum(WIDGET_TYPES)

const createDashboardSchema = z.object({
  name: z.string().min(1).max(200),
})

const updateDashboardSchema = z.object({
  name: z.string().min(1).max(200).optional(),
  isDefault: z.boolean().optional(),
})

const addWidgetSchema = z.object({
  widgetType: widgetTypeSchema,
  positionX: z.number().int().min(0).max(11),
  positionY: z.number().int().min(0),
  width: z.number().int().min(1).max(12).default(1),
  height: z.number().int().min(1).max(12).default(1),
  config: z.record(z.unknown()).optional(),
})

const updateWidgetSchema = z.object({
  widgetType: widgetTypeSchema.optional(),
  positionX: z.number().int().min(0).max(11).optional(),
  positionY: z.number().int().min(0).optional(),
  width: z.number().int().min(1).max(12).optional(),
  height: z.number().int().min(1).max(12).optional(),
  config: z.record(z.unknown()).optional(),
})

export const analyticsDashboardRoutes = new Hono<AppEnv>()

analyticsDashboardRoutes.use('*', authMiddleware)

function requireUserTenant(c: { get: (k: 'session') => AppEnv['Variables']['session'] }) {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return null
  }
  return { tenantId: session.tid, userId: session.sub }
}

// ── GET / ─────────────────────────────────────────────────────────────────────

analyticsDashboardRoutes.get('/', requirePermission('reports:read'), async (c) => {
  const ctx = requireUserTenant(c)
  if (!ctx) return c.json({ error: 'No tenant context' }, 403)

  const db = c.get('db')
  const dashboards = await ensureDefaultDashboard(db, ctx.tenantId, ctx.userId)
  return c.json({ dashboards })
})

// ── POST / ────────────────────────────────────────────────────────────────────

analyticsDashboardRoutes.post('/', requirePermission('reports:write'), async (c) => {
  const ctx = requireUserTenant(c)
  if (!ctx) return c.json({ error: 'No tenant context' }, 403)

  const parsed = createDashboardSchema.safeParse(await c.req.json().catch(() => null))
  if (!parsed.success) {
    return c.json({ error: 'Invalid body', details: parsed.error.flatten() }, 400)
  }

  const db = c.get('db')
  const dashboard = await createDashboard(db, ctx.tenantId, ctx.userId, parsed.data.name)
  return c.json({ dashboard }, 201)
})

// ── GET /:id ──────────────────────────────────────────────────────────────────

analyticsDashboardRoutes.get('/:id', requirePermission('reports:read'), async (c) => {
  const ctx = requireUserTenant(c)
  if (!ctx) return c.json({ error: 'No tenant context' }, 403)

  const id = c.req.param('id')
  const db = c.get('db')
  const result = await getDashboardWithWidgets(db, ctx.tenantId, ctx.userId, id)
  if (!result) return c.json({ error: 'Not found' }, 404)

  return c.json(result)
})

// ── PATCH /:id ────────────────────────────────────────────────────────────────

analyticsDashboardRoutes.patch('/:id', requirePermission('reports:write'), async (c) => {
  const ctx = requireUserTenant(c)
  if (!ctx) return c.json({ error: 'No tenant context' }, 403)

  const parsed = updateDashboardSchema.safeParse(await c.req.json().catch(() => null))
  if (!parsed.success) {
    return c.json({ error: 'Invalid body', details: parsed.error.flatten() }, 400)
  }

  const db = c.get('db')
  const dashboard = await updateDashboard(
    db,
    ctx.tenantId,
    ctx.userId,
    c.req.param('id'),
    parsed.data,
  )
  if (!dashboard) return c.json({ error: 'Not found' }, 404)

  return c.json({ dashboard })
})

// ── DELETE /:id ─────────────────────────────────────────────────────────────

analyticsDashboardRoutes.delete('/:id', requirePermission('reports:write'), async (c) => {
  const ctx = requireUserTenant(c)
  if (!ctx) return c.json({ error: 'No tenant context' }, 403)

  const db = c.get('db')
  const ok = await deleteDashboard(db, ctx.tenantId, ctx.userId, c.req.param('id'))
  if (!ok) return c.json({ error: 'Not found' }, 404)

  return new Response(null, { status: 204 })
})

// ── POST /:id/widgets ─────────────────────────────────────────────────────────

analyticsDashboardRoutes.post('/:id/widgets', requirePermission('reports:write'), async (c) => {
  const ctx = requireUserTenant(c)
  if (!ctx) return c.json({ error: 'No tenant context' }, 403)

  const parsed = addWidgetSchema.safeParse(await c.req.json().catch(() => null))
  if (!parsed.success) {
    return c.json({ error: 'Invalid body', details: parsed.error.flatten() }, 400)
  }

  const db = c.get('db')
  const widget = await addDashboardWidget(
    db,
    ctx.tenantId,
    ctx.userId,
    c.req.param('id'),
    parsed.data,
  )
  if (!widget) return c.json({ error: 'Dashboard not found' }, 404)

  return c.json({ widget }, 201)
})

// ── PATCH /:id/widgets/:wid ─────────────────────────────────────────────────

analyticsDashboardRoutes.patch(
  '/:id/widgets/:wid',
  requirePermission('reports:write'),
  async (c) => {
    const ctx = requireUserTenant(c)
    if (!ctx) return c.json({ error: 'No tenant context' }, 403)

    const parsed = updateWidgetSchema.safeParse(await c.req.json().catch(() => null))
    if (!parsed.success) {
      return c.json({ error: 'Invalid body', details: parsed.error.flatten() }, 400)
    }

    const db = c.get('db')
    const widget = await updateDashboardWidget(
      db,
      ctx.tenantId,
      ctx.userId,
      c.req.param('id'),
      c.req.param('wid'),
      parsed.data,
    )
    if (!widget) return c.json({ error: 'Not found' }, 404)

    return c.json({ widget })
  },
)

// ── DELETE /:id/widgets/:wid ────────────────────────────────────────────────

analyticsDashboardRoutes.delete(
  '/:id/widgets/:wid',
  requirePermission('reports:write'),
  async (c) => {
    const ctx = requireUserTenant(c)
    if (!ctx) return c.json({ error: 'No tenant context' }, 403)

    const db = c.get('db')
    const ok = await deleteDashboardWidget(
      db,
      ctx.tenantId,
      ctx.userId,
      c.req.param('id'),
      c.req.param('wid'),
    )
    if (!ok) return c.json({ error: 'Not found' }, 404)

    return new Response(null, { status: 204 })
  },
)
