import { eq } from 'drizzle-orm'
import type { Querier } from '@platform-modules/db'
import type { Actor } from './authz.js'
import { assertCanManageMenus } from './authz.js'
import { MenuNotFoundError, MenuValidationError } from './errors.js'
import type { MenuRow } from './model.js'
import { MENU_KEY_RE, menus, type MenusSchema } from './schema.js'

function nowMs(): number {
  return Date.now()
}

function newId(): string {
  return crypto.randomUUID()
}

function rowToMenu(row: typeof menus.$inferSelect): MenuRow {
  return {
    id: row.id,
    key: row.key,
    label: row.label,
    createdAtMs: row.createdAtMs,
    updatedAtMs: row.updatedAtMs,
  }
}

function validateMenuKey(key: string): void {
  if (!MENU_KEY_RE.test(key)) throw new MenuValidationError('key', 'invalid menu key')
}

export async function createMenu(
  db: Querier<MenusSchema>,
  actor: Actor,
  input: { key: string; label: string },
): Promise<MenuRow> {
  assertCanManageMenus(actor)
  validateMenuKey(input.key)
  const existing = await db.select().from(menus).where(eq(menus.key, input.key)).limit(1)
  if (existing.length > 0) throw new MenuValidationError('key', 'menu key already exists')
  const ts = nowMs()
  const row = {
    id: newId(),
    key: input.key,
    label: input.label,
    createdAtMs: ts,
    updatedAtMs: ts,
  }
  await db.insert(menus).values(row)
  return rowToMenu(row)
}

export async function updateMenu(
  db: Querier<MenusSchema>,
  actor: Actor,
  menuId: string,
  patch: { label?: string },
): Promise<MenuRow> {
  assertCanManageMenus(actor)
  const [existing] = await db.select().from(menus).where(eq(menus.id, menuId)).limit(1)
  if (!existing) throw new MenuNotFoundError('menu', menuId)
  const updatedAtMs = nowMs()
  const next = {
    label: patch.label ?? existing.label,
    updatedAtMs,
  }
  await db.update(menus).set(next).where(eq(menus.id, menuId))
  return rowToMenu({ ...existing, ...next })
}

export async function deleteMenu(db: Querier<MenusSchema>, actor: Actor, menuId: string): Promise<void> {
  assertCanManageMenus(actor)
  const [existing] = await db.select().from(menus).where(eq(menus.id, menuId)).limit(1)
  if (!existing) throw new MenuNotFoundError('menu', menuId)
  await db.delete(menus).where(eq(menus.id, menuId))
}
