import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSessionUser } from '@/lib/auth'
import { createAdminClient } from '@/lib/supabase/admin'
import { logCompanyEvent } from '@/lib/data/companies'
import { findEnvUserById } from '@/lib/auth/env-users'
import { setAccountPassword, setAccountRole } from '@/lib/auth/accounts'
import { generateTemporaryPassword, passwordProblem } from '@/lib/auth/passwords'

/**
 * Admin user management (2026-09-11).
 *
 * Requirement: "Add an admin user management panel with password reset and
 * the ability to change user roles."
 *
 * ops:
 *   role            change a login's role — stored in auth_accounts, so it
 *                   survives sign-in instead of being overwritten by AUTH_USERS
 *   password_reset  set a new password — a generated temporary one, or one the
 *                   admin types — stored as a scrypt hash in auth_accounts
 *   revoke_company  remove a company membership
 *
 * Role change and password reset both END the person's existing sessions
 * (auth_accounts.tokens_valid_after): a reset has to lock out an old session,
 * and a demoted admin must not keep admin until their session expires.
 *
 * The plain-text password is returned ONCE, in this response, so the admin can
 * give it to the user. It is never logged, never stored readable, and never
 * written to the audit trail.
 */

const schema = z.discriminatedUnion('op', [
  z.object({
    op: z.literal('role'),
    user_id: z.string().uuid(),
    role: z.enum(['broker', 'dispatcher', 'driver', 'admin']),
  }),
  z.object({
    op: z.literal('password_reset'),
    user_id: z.string().uuid(),
    /** Omit to generate a temporary password. */
    password: z.string().max(200).optional(),
  }),
  z.object({ op: z.literal('revoke_company'), membership_id: z.string().uuid() }),
])

export async function POST(req: NextRequest) {
  const user = await getSessionUser()
  if (!user) return NextResponse.json({ error: 'Sign in first.' }, { status: 401 })
  if (user.role !== 'admin') return NextResponse.json({ error: 'Admins only.' }, { status: 403 })

  const parsed = schema.safeParse(await req.json().catch(() => ({})))
  if (!parsed.success) return NextResponse.json({ error: 'Invalid request.' }, { status: 400 })
  const input = parsed.data
  const admin = createAdminClient()
  const actorLabel = user.name || user.email
  const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? null

  if (input.op === 'revoke_company') {
    const now = new Date().toISOString()
    const { data: membership, error } = await admin
      .from('company_memberships')
      .update({ status: 'revoked', revoked_by: actorLabel, revoked_at: now })
      .eq('id', input.membership_id)
      .select()
      .maybeSingle()
    if (error) return NextResponse.json({ error: 'Could not revoke company access.' }, { status: 500 })
    if (!membership) return NextResponse.json({ error: 'Membership not found.' }, { status: 404 })
    await logCompanyEvent({
      companyId: membership.company_id,
      actorUserId: user.id,
      actorLabel,
      eventType: 'admin_company_access_revoked',
      newValue: { user_id: membership.user_id, membership_id: membership.id },
      ip,
    })
    return NextResponse.json({ ok: true })
  }

  // Role and password both need a login: they decide what happens at sign-in,
  // and a profile with no AUTH_USERS entry cannot sign in at all.
  const target = findEnvUserById(input.user_id)
  if (!target) {
    return NextResponse.json(
      { error: 'This person has no login. Add them to AUTH_USERS first.' },
      { status: 400 },
    )
  }

  if (input.op === 'role') {
    // Never let an admin lock the platform out of its own console.
    if (input.user_id === user.originId && input.role !== 'admin') {
      return NextResponse.json(
        { error: 'You cannot remove your own admin role. Ask another admin to do it.' },
        { status: 400 },
      )
    }
    const { data: before } = await admin
      .from('profiles')
      .select('default_role')
      .eq('id', input.user_id)
      .maybeSingle()

    const result = await setAccountRole(target, input.role, actorLabel)
    if (!result.ok) {
      return NextResponse.json({ error: result.error }, { status: result.missing ? 503 : 500 })
    }
    // Keep the profile in step so every screen that reads it agrees at once.
    await admin.from('profiles').update({ default_role: input.role }).eq('id', input.user_id)

    await logCompanyEvent({
      companyId: null,
      actorUserId: user.id,
      actorLabel,
      eventType: 'admin_user_role_changed',
      oldValue: { user_id: input.user_id, role: before?.default_role ?? target.role },
      newValue: { user_id: input.user_id, role: input.role },
      ip,
    })
    return NextResponse.json({
      ok: true,
      note: `${target.name} is now ${input.role}. Their current sessions have ended — the new role applies when they sign in.`,
    })
  }

  // ---- password_reset ----
  const generated = !input.password
  const password = input.password ?? generateTemporaryPassword()
  if (!generated) {
    const problem = passwordProblem(password)
    if (problem) return NextResponse.json({ error: problem }, { status: 400 })
  }
  const result = await setAccountPassword(target, password, actorLabel)
  if (!result.ok) {
    return NextResponse.json({ error: result.error }, { status: result.missing ? 503 : 500 })
  }
  await logCompanyEvent({
    companyId: null,
    actorUserId: user.id,
    actorLabel,
    eventType: 'admin_password_reset',
    // Who and how — never the password itself.
    newValue: { user_id: input.user_id, generated },
    ip,
  })
  return NextResponse.json({
    ok: true,
    username: target.username,
    // Shown once to the admin, then gone.
    password,
    generated,
    self: input.user_id === user.originId,
  })
}
