/**
 * Admin tenant user-management routes — admin-dashboard.
 * GET  /api/admin/tenants/:slug/users              → paginated user list
 * POST /api/admin/tenants/:slug/users/:id/approve  → approve pending member
 * POST /api/admin/tenants/:slug/users/:id/freeze   → freeze member
 * POST /api/admin/tenants/:slug/users/:id/unfreeze → unfreeze member
 * DELETE /api/admin/tenants/:slug/invitations/:id  → revoke pending invitation
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { createDb } from '@zync/db/queries'
import {
  listTenantUsersAdmin,
  approvePendingMember,
  freezeTenantMember,
  unfreezeTenantMember,
  revokeUserRefreshTokensForTenant,
  revokePendingInvitation,
  getTenantDetailAdmin,
  writeAdminAuditLog,
} from '@zync/db/queries'
import type { AppEnv } from '../../types'
import { adminAuthMiddleware } from '../../middleware/admin-auth'
import { requireAdminSession } from '../../middleware/guards'
import { requireAdminPermission } from '@zync/auth'
import type { AdminSessionPayload } from '@zync/types'

const adminTenantUsersRoutes = new Hono<AppEnv>()

adminTenantUsersRoutes.use('*', adminAuthMiddleware)
adminTenantUsersRoutes.use('*', requireAdminSession())

const ListUsersQuerySchema = z.object({
  page: z.coerce.number().int().positive().optional().default(1),
  limit: z.coerce.number().int().positive().max(200).optional().default(50),
})

const FreezeBodySchema = z.object({
  reason: z.string().min(1, { message: 'Freeze reason is required' }),
})

// ── List users ────────────────────────────────────────────────────────────────

adminTenantUsersRoutes.get(
  '/:slug/users',
  requireAdminPermission('admin.users:read'),
  async (c) => {
    const slug = c.req.param('slug')
    const parsed = ListUsersQuerySchema.safeParse(c.req.query())
    if (!parsed.success) {
      return c.json({ error: 'Invalid query params', details: parsed.error.flatten() }, 400)
    }

    const db = createDb(c.env)
    const tenant = await getTenantDetailAdmin(db, slug)
    if (!tenant) return c.json({ error: 'Tenant not found' }, 404)

    const result = await listTenantUsersAdmin(db, tenant.id, {
      page: parsed.data.page,
      limit: parsed.data.limit,
    })

    return c.json(result)
  },
)

// ── Approve ───────────────────────────────────────────────────────────────────

adminTenantUsersRoutes.post(
  '/:slug/users/:userId/approve',
  requireAdminPermission('admin.users:write'),
  async (c) => {
    const slug = c.req.param('slug')
    const userId = c.req.param('userId')
    const ip = c.req.header('CF-Connecting-IP') ?? null
    const session = c.get('session') as AdminSessionPayload

    const db = createDb(c.env)
    const tenant = await getTenantDetailAdmin(db, slug)
    if (!tenant) return c.json({ error: 'Tenant not found' }, 404)

    const approved = await approvePendingMember(db, tenant.id, userId)
    if (!approved) {
      return c.json({ error: 'User not found or not in pending_approval state' }, 404)
    }

    await writeAdminAuditLog(db, {
      tenantId: tenant.id,
      actorId: session.sub,
      entityType: 'tenant_membership',
      entityId: userId,
      action: 'membership.approved',
      ip,
    })

    return c.body(null, 204)
  },
)

// ── Freeze ────────────────────────────────────────────────────────────────────

adminTenantUsersRoutes.post(
  '/:slug/users/:userId/freeze',
  requireAdminPermission('admin.users:write'),
  async (c) => {
    const slug = c.req.param('slug')
    const userId = c.req.param('userId')
    const ip = c.req.header('CF-Connecting-IP') ?? null
    const session = c.get('session') as AdminSessionPayload

    const parsed = FreezeBodySchema.safeParse(await c.req.json())
    if (!parsed.success) {
      return c.json({ error: 'Invalid request', details: parsed.error.flatten() }, 400)
    }

    const db = createDb(c.env)
    const tenant = await getTenantDetailAdmin(db, slug)
    if (!tenant) return c.json({ error: 'Tenant not found' }, 404)

    const frozen = await freezeTenantMember(db, tenant.id, userId, parsed.data.reason)
    if (!frozen) {
      return c.json({ error: 'User not found or already frozen' }, 404)
    }

    // Revoke refresh tokens for immediate JWT invalidation
    await revokeUserRefreshTokensForTenant(db, userId, tenant.id)

    await writeAdminAuditLog(db, {
      tenantId: tenant.id,
      actorId: session.sub,
      entityType: 'tenant_membership',
      entityId: userId,
      action: 'membership.frozen',
      changes: { reason: [null, parsed.data.reason] },
      ip,
    })

    return c.body(null, 204)
  },
)

// ── Unfreeze ──────────────────────────────────────────────────────────────────

adminTenantUsersRoutes.post(
  '/:slug/users/:userId/unfreeze',
  requireAdminPermission('admin.users:write'),
  async (c) => {
    const slug = c.req.param('slug')
    const userId = c.req.param('userId')
    const ip = c.req.header('CF-Connecting-IP') ?? null
    const session = c.get('session') as AdminSessionPayload

    const db = createDb(c.env)
    const tenant = await getTenantDetailAdmin(db, slug)
    if (!tenant) return c.json({ error: 'Tenant not found' }, 404)

    const unfrozen = await unfreezeTenantMember(db, tenant.id, userId)
    if (!unfrozen) {
      return c.json({ error: 'User not found or not frozen' }, 404)
    }

    await writeAdminAuditLog(db, {
      tenantId: tenant.id,
      actorId: session.sub,
      entityType: 'tenant_membership',
      entityId: userId,
      action: 'membership.unfrozen',
      ip,
    })

    return c.body(null, 204)
  },
)

// ── Revoke invitation ─────────────────────────────────────────────────────────

adminTenantUsersRoutes.delete(
  '/:slug/invitations/:invitationId',
  requireAdminPermission('admin.users:write'),
  async (c) => {
    const slug = c.req.param('slug')
    const invitationId = c.req.param('invitationId')
    const ip = c.req.header('CF-Connecting-IP') ?? null
    const session = c.get('session') as AdminSessionPayload

    const db = createDb(c.env)
    const tenant = await getTenantDetailAdmin(db, slug)
    if (!tenant) return c.json({ error: 'Tenant not found' }, 404)

    const revoked = await revokePendingInvitation(db, tenant.id, invitationId)
    if (!revoked) {
      return c.json({ error: 'Invitation not found or already accepted' }, 404)
    }

    await writeAdminAuditLog(db, {
      tenantId: tenant.id,
      actorId: session.sub,
      entityType: 'invitation',
      entityId: invitationId,
      action: 'invitation.revoked',
      ip,
    })

    return c.body(null, 204)
  },
)

export { adminTenantUsersRoutes }
