/**
 * Tenant-app impersonation exchange — POST /impersonate
 *
 * Called by the admin portal (form POST) after minting an impersonation JWT.
 * Token is in the POST body (not URL) to prevent log/Referer/history leakage.
 * Validates the short-lived impersonation token, builds a 15-min OWNER
 * session with impersonation=true, sets the zync_session cookie, and
 * redirects to /.
 *
 * No prior session required; unauthenticated route.
 * No refresh cookie is ever set for impersonation sessions.
 */
import { Hono } from 'hono'
import { setCookie } from 'hono/cookie'
import { z } from 'zod'
import {
  verifyImpersonationToken,
  signSession,
  IMPERSONATION_TTL_SECONDS,
} from '@zync/auth'
import {
  createDb,
  getTenantById,
  getRoleByName,
  getPermissionsForRole,
} from '@zync/db/queries'
import type { TenantId, TenantTier, UserId } from '@zync/types'
import type { AppEnv } from '../types'

const bodySchema = z.object({
  token: z.string().min(1),
})

const COOKIE_OPTS = {
  httpOnly: true,
  secure: true,
  sameSite: 'Strict' as const,
  domain: '.zync.is',
  path: '/',
}

export const impersonateRoute = new Hono<AppEnv>()

impersonateRoute.post('/', async (c) => {
  let rawBody: unknown
  try {
    const ct = c.req.header('content-type') ?? ''
    if (ct.includes('application/x-www-form-urlencoded')) {
      const form = await c.req.formData()
      rawBody = { token: form.get('token') }
    } else {
      const jsonResult = bodySchema.safeParse(await c.req.json())
      rawBody = jsonResult.success ? jsonResult.data : {}
    }
  } catch { rawBody = {} }
  const parsed = bodySchema.safeParse(rawBody)
  if (!parsed.success) {
    return c.redirect('/login?error=impersonation_invalid')
  }

  let impToken
  try {
    impToken = await verifyImpersonationToken(parsed.data.token, c.env.IMPERSONATION_SECRET)
  } catch {
    return c.redirect('/login?error=impersonation_invalid')
  }

  // Guard: type discriminator
  if (impToken.type !== 'impersonation') {
    return c.redirect('/login?error=impersonation_invalid')
  }

  const db = createDb(c.env)

  // Load tenant
  const tenant = await getTenantById(db, impToken.tenant_id as TenantId)
  if (!tenant) {
    return c.redirect('/login?error=impersonation_invalid')
  }

  // Resolve OWNER role + permissions within this tenant
  const ownerRole = await getRoleByName(db, tenant.id as TenantId, 'OWNER')
  if (!ownerRole) {
    return c.redirect('/login?error=impersonation_invalid')
  }
  const permissions = await getPermissionsForRole(db, ownerRole.id)

  // Parse admin ID from sub ("admin:{uuid}")
  const adminId = impToken.sub.startsWith('admin:')
    ? impToken.sub.slice('admin:'.length)
    : impToken.sub

  // Build impersonation session — sub is admin UUID, tid is the target tenant
  const sessionPayload = {
    sub: adminId as UserId,
    tid: tenant.id as TenantId,
    role: ownerRole.name,
    permissions,
    tier: tenant.tier as TenantTier,
    type: 'user' as const,
    v: 0, // impersonation sessions bypass user_version revocation
    enforce_2fa: false,
    two_factor_verified: true,
    plan: tenant.tier,
    tenantSlug: tenant.slug,
    impersonation: true as const,
    impersonating_admin_id: adminId,
  }

  const accessToken = await signSession(sessionPayload, c.env.JWT_SECRET)

  setCookie(c, 'zync_session', accessToken, {
    ...COOKIE_OPTS,
    maxAge: IMPERSONATION_TTL_SECONDS,
  })

  return c.redirect('/')
})
