/**
 * Portal-side file routes — portal-file-sharing (wave-11 leaf-C).
 *
 * Mounted on the portal router at /api/portal/files*.
 * Auth: stateful portal session via portalAuthMiddleware.
 * tenantId and customerId come from portal context only — never URL or body.
 *
 * Routes:
 *   GET  /files                  — list visible, non-expired files for JWT customer
 *   POST /files/upload-url       — request signed PUT URL (if portal_can_upload_files)
 *   POST /files                  — record metadata after R2 PUT
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { listPortalFilesForPortal, createPortalFile, InvalidUploaderError, getPortalSettings, getProjectById } from '@zync/db/queries'
import type { AppEnv } from '../../types'
import { portalAuthMiddleware, type PortalAuthVariables } from '../../middleware/portalAuth'
import {
  buildPortalFileKey,
  validateUpload,
  validatePortalFileR2Key,
  createSignedPutUrl,
  PortalFileValidationError,
} from '../../lib/portal-file-storage'

type PortalDataEnv = {
  Bindings: AppEnv['Bindings']
  Variables: PortalAuthVariables
}

// ── Schemas ───────────────────────────────────────────────────────────────────

const portalUploadUrlSchema = z.object({
  filename: z.string().min(1),
  mime_type: z.string().min(1),
  file_size_bytes: z.number().int().positive(),
})

const portalCreateFileSchema = z.object({
  r2_key: z.string().min(1),
  filename: z.string().min(1),
  mime_type: z.string().min(1),
  file_size_bytes: z.number().int().positive(),
  project_id: z.string().uuid().optional(),
})

// ── Router ────────────────────────────────────────────────────────────────────

export const portalFilesPortalRoute = new Hono<PortalDataEnv>()

portalFilesPortalRoute.use('*', portalAuthMiddleware)

// ── GET /api/portal/files ─────────────────────────────────────────────────────

portalFilesPortalRoute.get('/files', async (c) => {
  const portal = c.get('portal')
  const db = c.get('db')

  // Gate on portal_visibility.show_files
  const settings = await getPortalSettings(db, portal.tenantId)
  const showFiles = settings?.portal_visibility?.show_files
  if (!showFiles) return c.json({ error: 'Not found' }, 404)

  const url = new URL(c.req.url)
  const limit = Math.min(50, parseInt(url.searchParams.get('limit') ?? '50', 10))
  const offset = parseInt(url.searchParams.get('offset') ?? '0', 10)

  const items = await listPortalFilesForPortal(db, portal.tenantId, portal.customerId, { limit, offset })
  return c.json({ items }, 200)
})

// ── POST /api/portal/files/upload-url ─────────────────────────────────────────

portalFilesPortalRoute.post('/files/upload-url', async (c) => {
  const portal = c.get('portal')
  const db = c.get('db')
  const settings = await getPortalSettings(db, portal.tenantId)
  const showFiles = settings?.portal_visibility?.show_files
  if (!showFiles) return c.json({ error: 'Not found' }, 404)

  const canUpload = settings?.portal_can_upload_files
  if (!canUpload) return c.json({ error: 'UPLOAD_DISABLED' }, 403)

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

  const { filename, mime_type, file_size_bytes } = parsed.data
  try {
    validateUpload({ mime_type, file_size_bytes })
  } catch (err) {
    if (err instanceof PortalFileValidationError) {
      return c.json({ error: err.message, code: err.code }, 400)
    }
    throw err
  }

  const r2Key = buildPortalFileKey(portal.tenantId, portal.customerId, filename)
  const uploadUrl = await createSignedPutUrl(c.env, r2Key, { contentType: mime_type })
  return c.json({ upload_url: uploadUrl, r2_key: r2Key }, 200)
})

// ── POST /api/portal/files ────────────────────────────────────────────────────

portalFilesPortalRoute.post('/files', async (c) => {
  const portal = c.get('portal')
  const db = c.get('db')
  const settings = await getPortalSettings(db, portal.tenantId)
  const showFiles = settings?.portal_visibility?.show_files
  if (!showFiles) return c.json({ error: 'Not found' }, 404)

  const canUpload = settings?.portal_can_upload_files
  if (!canUpload) return c.json({ error: 'UPLOAD_DISABLED' }, 403)

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

  const { r2_key, filename, mime_type, file_size_bytes, project_id } = parsed.data

  if (project_id) {
    const project = await getProjectById(db, portal.tenantId, project_id)
    if (!project || project.customer_id !== portal.customerId) {
      return c.json({ error: 'Not found' }, 404)
    }
  }

  try {
    validateUpload({ mime_type, file_size_bytes })
    validatePortalFileR2Key(portal.tenantId, portal.customerId, r2_key)
  } catch (err) {
    if (err instanceof PortalFileValidationError) {
      return c.json({ error: err.message, code: err.code }, 400)
    }
    throw err
  }

  try {
    const file = await createPortalFile(db, {
      tenantId: portal.tenantId,
      customerId: portal.customerId,
      projectId: project_id ?? null,
      uploadedBy: null,
      uploadedByPortalUser: portal.userId,
      r2Key: r2_key,
      filename,
      mimeType: mime_type,
      fileSizeBytes: file_size_bytes,
      visibleToPortal: true,
    })
    return c.json({ file }, 201)
  } catch (err) {
    if (err instanceof InvalidUploaderError) {
      return c.json({ error: err.message }, 400)
    }
    throw err
  }
})
