/**
 * Entity history route — operational-audit-trail (spec 50).
 *
 * GET /api/:entity/:id/history
 *
 * Returns cursor-paginated change history for a single entity.
 * Access:
 *   - OWNER / ADMIN: all changes for the entity
 *   - MEMBER:        own changes only (WHERE user_id = session.sub)
 *   - CONTRACTOR / CLIENT_PORTAL: 403
 *
 * Tier gate (read window):
 *   - freelancer:        last 90 days
 *   - business+:         last 365 days
 *
 * Capture is ALL-TIERS (no tier gate on write).
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { authMiddleware } from '../middleware/auth'
import {
  listEntityHistory,
  TRACKED_ENTITIES,
  HISTORY_API_ENTITIES,
  getInvoice,
  getCustomerWithStats,
  getProjectById,
  getExpense,
  getContract,
  ContractNotFoundError,
} from '@zync/db/queries'
import { TenantTier } from '@zync/types'
import type { AppEnv } from '../types'
import {
  isContractorRole,
  MEMBER_ROLE,
} from '../lib/system-roles'

async function entityExistsInTenant(
  db: Parameters<typeof getInvoice>[0],
  tenantId: string,
  entitySlug: string,
  entityId: string,
): Promise<boolean> {
  switch (entitySlug) {
    case 'invoices':
      return !!(await getInvoice(db, tenantId, entityId))
    case 'customers':
      return !!(await getCustomerWithStats(db, tenantId, entityId))
    case 'projects':
      return !!(await getProjectById(db, tenantId, entityId))
    case 'expenses':
      return !!(await getExpense(db, tenantId, entityId))
    case 'contracts':
      try {
        await getContract(db, tenantId, entityId)
        return true
      } catch (err) {
        if (err instanceof ContractNotFoundError) return false
        throw err
      }
    default:
      console.warn('entityExistsInTenant: unknown entity type', entitySlug)
      return false
  }
}

// ── Zod validation ────────────────────────────────────────────────────────────

const historyQuerySchema = z.object({
  from: z.string().optional(),
  to: z.string().optional(),
  cursor: z.string().optional(),
  limit: z.coerce.number().int().min(1).max(50).default(50),
})

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

export const entityHistoryRoutes = new Hono<AppEnv>()

entityHistoryRoutes.use('*', authMiddleware)

/**
 * GET /api/:entity/:id/history
 *
 * :entity must be one of: invoices | customers | projects | expenses | contracts
 */
entityHistoryRoutes.get('/:entity/:id/history', async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const entitySlug = c.req.param('entity')
  const entityId   = c.req.param('id')

  // Validate entity slug against whitelist
  if (!HISTORY_API_ENTITIES.has(entitySlug)) {
    return c.json({ error: 'Not found' }, 404)
  }

  const entityType = TRACKED_ENTITIES[entitySlug]!

  // Role gate: CONTRACTOR / CLIENT_PORTAL → 403
  const role = (session as { role?: string }).role ?? ''
  if (role === 'client_portal' || isContractorRole(role)) {
    return c.json({ error: 'Forbidden' }, 403)
  }

  // MEMBER can only see their own changes
  const forceUserId = role === MEMBER_ROLE ? (session.sub as string) : undefined

  // Validate query params
  const rawQuery = Object.fromEntries(new URL(c.req.url).searchParams)
  const parsed = historyQuerySchema.safeParse({
    from:   rawQuery['from'],
    to:     rawQuery['to'],
    cursor: rawQuery['cursor'],
    limit:  rawQuery['limit'],
  })
  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 400)
  }

  const { from, to, cursor, limit } = parsed.data

  // Tier gate: limit date window based on subscription tier
  // Freelancer → 90 days; Business+ → 365 days
  const tier = (session as { tier?: string }).tier
  const windowDays = tier === TenantTier.FREELANCER ? 90 : 365
  const windowFrom = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1000).toISOString()

  // Use the more restrictive of the explicit `from` param and the tier window
  let effectiveFrom = windowFrom
  if (from) {
    const paramFrom = isNaN(Number(from)) ? from : new Date(Number(from) * 1000).toISOString()
    effectiveFrom = paramFrom > windowFrom ? paramFrom : windowFrom
  }

  const db = c.get('db')

  const exists = await entityExistsInTenant(db, session.tid as string, entitySlug, entityId)
  if (!exists) {
    return c.json({ error: 'Not found' }, 404)
  }

  const page = await listEntityHistory(db, session.tid as string, entityType, entityId, {
    userId:  forceUserId,
    from:    effectiveFrom,
    to,
    cursor,
    limit,
  })

  return c.json(page, 200)
})
