import { Hono } from 'hono'
import type { Context } from 'hono'
import { z } from 'zod'
import { desktopShellLayoutEnvelopeSchema, mobileShellLayoutEnvelopeSchema, shellDeviceSchema } from '../../../../packages/modules/src/shell-layout-schema'
import { createDb, deleteShellLayout, getShellLayout, getShellPreferences, setShellLayout } from '@zync/db/queries'
import type { AppEnv } from '../types'
import { authMiddleware } from '../middleware/auth'

export const shellLayoutRoute = new Hono<AppEnv>()
shellLayoutRoute.use('*', authMiddleware)

function sessionOf(c: Context<AppEnv>) {
  const session = c.get('session')
  return session && session.type === 'user' && session.tid ? session : null
}

function internalServerError(c: Context<AppEnv>) {
  return c.json({ error: 'Internal server error' }, 500)
}

shellLayoutRoute.get('/', async (c) => {
  const session = sessionOf(c)
  if (!session) return c.json({ error: 'Unauthorized' }, 401)
  const device = shellDeviceSchema.safeParse(c.req.query('device'))
  if (!device.success) return c.json({ error: 'Unsupported device' }, 400)
  try {
    const db = c.get('db') ?? createDb(c.env)
    const [layout, preferences] = await Promise.all([
      getShellLayout(db, session.sub, session.tid!, device.data),
      getShellPreferences(db, session.sub, session.tid!),
    ])
    if (!layout) return c.json({ ...preferences, error: 'Not found' }, 404)
    return c.json({ ...layout, ...preferences }, 200)
  } catch {
    return internalServerError(c)
  }
})

shellLayoutRoute.put('/', async (c) => {
  const session = sessionOf(c)
  if (!session) return c.json({ error: 'Unauthorized' }, 401)
  const device = shellDeviceSchema.safeParse(c.req.query('device'))
  if (!device.success) return c.json({ error: 'Unsupported device' }, 400)
  const payloadSchema = device.data === 'desktop' ? desktopShellLayoutEnvelopeSchema : mobileShellLayoutEnvelopeSchema
  const parsed = await c.req.json()
    .then((body) => z.object({ version: z.number().int().nonnegative(), payload: payloadSchema }).strict().safeParse(body))
    .catch(() => null)
  if (!parsed) return c.json({ error: 'Malformed JSON' }, 400)
  if (!parsed.success) return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
  try {
    const db = c.get('db') ?? createDb(c.env)
    const current = await getShellLayout(db, session.sub, session.tid!, device.data)
    if (parsed.data.version !== (current?.version ?? 0)) return c.json({ error: 'Conflict', current }, 409)
    const saved = await setShellLayout(db, session.sub, session.tid!, device.data, parsed.data.payload, parsed.data.version)
    if (!saved) return c.json({ error: 'Conflict', current: await getShellLayout(db, session.sub, session.tid!, device.data) }, 409)
    return c.json(saved, 200)
  } catch {
    return internalServerError(c)
  }
})

shellLayoutRoute.delete('/', async (c) => {
  const session = sessionOf(c)
  if (!session) return c.json({ error: 'Unauthorized' }, 401)
  const device = shellDeviceSchema.safeParse(c.req.query('device'))
  if (!device.success) return c.json({ error: 'Unsupported device' }, 400)
  try {
    const db = c.get('db') ?? createDb(c.env)
    await deleteShellLayout(db, session.sub, session.tid!, device.data)
    return c.body(null, 204)
  } catch {
    return internalServerError(c)
  }
})
