/**
 * Uniform-format export routes — uniform-format-export (wave-13, spec 180).
 * Mounted at /api/reports in the reports router.
 *
 * POST /api/reports/uniform-format        → create job (enqueue)
 * GET  /api/reports/uniform-format        → list recent jobs for tenant
 * GET  /api/reports/uniform-format/:id/download → generate signed URL (24h)
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'
import {
  createDb,
  createUniformExportJob,
  listUniformExportJobs,
  getUniformExportJob,
  updateUniformExportJobDownloadExpiry,
} from '@zync/db/queries'
import type { UniformExportJob, StartUniformExportInput } from '../../reports/uniform-format/types'

// ── R2 presign helper (reused from portal-file-storage pattern) ──────────────

type R2BucketWithPresign = R2Bucket & {
  createPresignedUrl?: (
    method: string,
    key: string,
    options: { expiresIn: number },
  ) => Promise<string>
}

async function presignR2Url(
  env: AppEnv['Bindings'],
  r2Key: string,
  expiresIn: number,
): Promise<string> {
  const storage = env.STORAGE as R2BucketWithPresign
  if (typeof storage.createPresignedUrl === 'function') {
    return storage.createPresignedUrl('GET', r2Key, { expiresIn })
  }
  const { AwsClient } = await import('aws4fetch')
  const envAny = env as unknown as Record<string, string>
  const accountId = envAny['CF_ACCOUNT_ID'] ?? ''
  const accessKeyId = envAny['R2_ACCESS_KEY_ID'] ?? ''
  const secretAccessKey = envAny['R2_SECRET_ACCESS_KEY'] ?? ''
  const bucketName = envAny['R2_BUCKET_NAME'] ?? 'zync-storage'
  const endpoint = `https://${accountId}.r2.cloudflarestorage.com`
  const aws = new AwsClient({ accessKeyId, secretAccessKey, region: 'auto', service: 's3' })
  const url = new URL(`${endpoint}/${bucketName}/${r2Key}`)
  url.searchParams.set('X-Amz-Expires', String(expiresIn))
  const presigned = await aws.sign(new Request(url.toString(), { method: 'GET' }), { aws: { signQuery: true } })
  return presigned.url
}

// ── Input schema ──────────────────────────────────────────────────────────────

const startExportSchema = z.object({
  period_from: z.string().date(),
  period_to: z.string().date(),
  mode: z.enum(['documents', 'documents_journal']),
})

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

export const uniformFormatRoutes = new Hono<AppEnv>()

uniformFormatRoutes.use('*', authMiddleware)
uniformFormatRoutes.use('*', requirePermission('reports:export'))

// ── POST /uniform-format ─────────────────────────────────────────────────────

uniformFormatRoutes.post('/', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  const tenantId = session.tid
  const userId = session.sub

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

  const { period_from, period_to, mode } = parsed.data

  // Validate date range (max 5 years)
  const from = new Date(period_from)
  const to = new Date(period_to)
  if (from > to) {
    return c.json({ error: 'period_from must be before period_to' }, 400)
  }
  const diffMs = to.getTime() - from.getTime()
  const diffYears = diffMs / (365.25 * 24 * 60 * 60 * 1000)
  if (diffYears > 5) {
    return c.json({ error: 'Date range cannot exceed 5 years' }, 400)
  }

  const db = createDb(c.env)

  // Create job record
  const job = await createUniformExportJob(db, {
    tenantId,
    periodFrom: period_from,
    periodTo: period_to,
    mode,
    status: 'pending',
    generatedBy: userId,
  }).catch(() => null)

  if (!job) {
    return c.json({ error: 'Failed to create export job' }, 500)
  }

  // Enqueue via QUEUE binding
  const queueMsg: UniformExportJob = {
    type: 'uniform-format',
    jobId: job.id,
    tenantId,
    from: period_from,
    to: period_to,
    mode: mode as StartUniformExportInput['mode'],
    userId,
  }

  await c.env.QUEUE.send(queueMsg)

  return c.json({ job_id: job.id, status: 'pending' }, 202)
})

// ── GET /uniform-format ───────────────────────────────────────────────────────

uniformFormatRoutes.get('/', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  const tenantId = session.tid

  const db = createDb(c.env)

  const jobs = await listUniformExportJobs(db, tenantId)

  return c.json({ jobs })
})

// ── GET /uniform-format/:id/download ─────────────────────────────────────────

uniformFormatRoutes.get('/:id/download', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  const tenantId = session.tid
  const jobId = c.req.param('id')

  const db = createDb(c.env)

  const job = await getUniformExportJob(db, tenantId, jobId)

  if (!job) {
    return c.json({ error: 'Export job not found' }, 404)
  }

  if (job.status !== 'done' || !job.r2Key) {
    return c.json({ error: 'Export not ready', status: job.status }, 409)
  }

  const now = new Date()
  const TTL_24H = 24 * 60 * 60

  // Always generate a fresh URL on each request from the stored r2_key
  const downloadUrl = await presignR2Url(c.env, job.r2Key, TTL_24H)
  const downloadExpiresAt = new Date(now.getTime() + TTL_24H * 1000)

  // Update expires_at in DB for tracking
  await updateUniformExportJobDownloadExpiry(db, tenantId, jobId, downloadExpiresAt)

  return c.json({
    download_url: downloadUrl,
    expires_at: downloadExpiresAt.toISOString(),
    filename: `uniform-format-${job.periodFrom}-${job.periodTo}.zip`,
    record_counts: job.recordCounts,
  })
})
