// tdd-guard:skip — Next.js route handler; testable logic lives in lib/api/error.ts tests.
import { NextRequest, NextResponse } from 'next/server'
import { getUserSession, canManageUser, type RoleCode } from '@/lib/auth/permissions'
import { createAdminClient } from '@/lib/supabase/admin'
import { createClient } from '@/lib/supabase/server'
import { checkCsrf } from '@/lib/security'
import { syncProfileToConsultant } from '@/lib/auth/sync-user-consultant'
import { logger } from '@/lib/logger'
import { routeError } from '@/lib/api/error'

// KAR-769 / D3: this password is NEVER disclosed — the invited user sets their
// own password via a single-use recovery link. We still set one so the auth
// account is not password-less; 48 random chars make the stored hash
// effectively uncrackable even if the auth DB is compromised.
function generatePassword(): string {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$'
  const array = new Uint8Array(48)
  crypto.getRandomValues(array)
  return Array.from(array, (byte) => chars[byte % chars.length]).join('')
}

interface CreateUserBody {
  first_name:    string
  last_name:     string
  email:         string
  role_id:       string
  department_id?: string | null
  /** If set: link this existing consultant instead of creating a new consultant record */
  consultant_id?: string | null
  /** KAR-787 / R8: reason given when force-creating despite a detected duplicate */
  force_create_reason?: string | null
}

interface UpdateUserBody {
  id:             string
  first_name?:    string
  last_name?:     string
  display_name?:  string
  department_id?: string | null
  role_id?:       string
  account_status?: string
}

export async function POST(request: NextRequest) {
  try {
  const csrf = checkCsrf(request); if (csrf) return csrf
  const session = await getUserSession()
  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }
  if (session.role !== 'admin' && session.role !== 'masteradmin') {
    return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
  }

  let body: CreateUserBody
  try {
    body = await request.json() as CreateUserBody
  } catch {
    return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })
  }

  const { first_name, last_name, email, role_id, department_id, consultant_id, force_create_reason } = body
  if (!first_name || !last_name || !email || !role_id) {
    return NextResponse.json({ error: 'Missing required fields' }, { status: 400 })
  }

  let adminClient: ReturnType<typeof createAdminClient>
  try {
    adminClient = createAdminClient()
  } catch (e) {
    logger.error('createAdminClient failed', e)
    return NextResponse.json({ error: 'Server misconfiguration: service key missing' }, { status: 500 })
  }

  const supabase = await createClient()

  // Accept either a UUID role_id or a role code string
  const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(role_id)
  const { data: roleRow } = await supabase
    .from('roles')
    .select('id, code')
    .eq(isUuid ? 'id' : 'code', role_id)
    .single()

  if (!roleRow) {
    return NextResponse.json({ error: `Invalid role: "${role_id}"` }, { status: 400 })
  }

  const resolvedRoleId = (roleRow as { id: string; code: string }).id
  const targetRoleCode = roleRow.code as RoleCode
  if (!canManageUser(session, targetRoleCode)) {
    return NextResponse.json({ error: 'Insufficient privileges to assign this role' }, { status: 403 })
  }

  const initialPassword = generatePassword()

  // Create auth user
  const { data: authData, error: authError } = await adminClient.auth.admin.createUser({
    email,
    password: initialPassword,
    email_confirm: true,
  })

  if (authError || !authData.user) {
    return routeError('Benutzer konnte nicht angelegt werden.', authError)
  }

  const authUserId  = authData.user.id
  const displayName = `${first_name} ${last_name}`

  // Insert user_profile
  const { data: profile, error: profileError } = await supabase
    .from('user_profiles')
    .insert({
      auth_user_id:               authUserId,
      first_name,
      last_name,
      display_name:               displayName,
      email,
      role_id:                    resolvedRoleId,
      department_id:              department_id ?? null,
      account_status:             'invited',
      must_change_password:       true,
      initial_password_issued_at: new Date().toISOString(),
      created_by:                 session.authUserId,
    })
    .select()
    .single()

  if (profileError || !profile) {
    await adminClient.auth.admin.deleteUser(authUserId)
    return routeError('Benutzerprofil konnte nicht angelegt werden.', profileError)
  }

  if (consultant_id) {
    // Link existing consultant record → set auth_user_id
    await supabase
      .from('consultants')
      .update({ auth_user_id: authUserId })
      .eq('id', consultant_id)
  } else if (targetRoleCode === 'consultant') {
    // New consultant record
    await supabase.from('consultants').insert({
      first_name,
      last_name,
      display_name: displayName,
      auth_user_id: authUserId,
      role:         'consultant',
      is_active:    true,
    })
  }

  const forceReason = (force_create_reason ?? '').trim() || null
  await supabase.from('user_audit_log').insert({
    actor_id:       session.authUserId,
    target_user_id: profile.id,
    // KAR-787 / R8: distinguish a deliberate force-create-despite-duplicate.
    action:         forceReason ? 'user.created_forced'
                  : consultant_id ? 'user.created_from_employee'
                  : 'user.created',
    details: {
      role_id,
      department_id: department_id ?? null,
      email,
      consultant_id: consultant_id ?? null,
      ...(forceReason ? { force_create_reason: forceReason } : {}),
    },
  })

  // KAR-769 / D3: no automatic email and no cleartext password in the response.
  // The account is created in 'invited' state; an admin sends the secure login
  // link explicitly via POST /api/admin/users/invite ("Einladung senden").

  return NextResponse.json({ profile }, { status: 201 })
  } catch (e) {
    return routeError('Benutzer konnte nicht angelegt werden.', e)
  }
}

export async function PATCH(request: NextRequest) {
  const csrf = checkCsrf(request); if (csrf) return csrf
  const session = await getUserSession()
  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }
  if (session.role !== 'admin' && session.role !== 'masteradmin') {
    return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
  }

  let body: UpdateUserBody
  try {
    body = await request.json() as UpdateUserBody
  } catch {
    return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })
  }

  const { id, ...fields } = body
  if (!id) {
    return NextResponse.json({ error: 'Missing id' }, { status: 400 })
  }

  const supabase = await createClient()

  const { data: targetProfile } = await supabase
    .from('user_profiles')
    .select('role_id, auth_user_id, roles ( code )')
    .eq('id', id)
    .single()

  if (!targetProfile) {
    return NextResponse.json({ error: 'User not found' }, { status: 404 })
  }

  const targetRoleCode = ((Array.isArray(targetProfile.roles) ? (targetProfile.roles as { code: string }[])[0] : (targetProfile.roles as { code: string } | null))?.code ?? 'readonly') as RoleCode
  if (!canManageUser(session, targetRoleCode)) {
    return NextResponse.json({ error: 'Insufficient privileges' }, { status: 403 })
  }

  // Resolve role_id: accept UUID or role code string
  if (fields.role_id) {
    const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(fields.role_id)
    if (!isUuid) {
      const { data: roleRow } = await supabase
        .from('roles')
        .select('id, code')
        .eq('code', fields.role_id)
        .single()
      if (!roleRow) {
        return NextResponse.json({ error: `Invalid role: "${fields.role_id}"` }, { status: 400 })
      }
      const newRoleCode = (roleRow as { id: string; code: string }).code as RoleCode
      if (!canManageUser(session, newRoleCode)) {
        return NextResponse.json({ error: 'Insufficient privileges to assign this role' }, { status: 403 })
      }
      fields.role_id = (roleRow as { id: string; code: string }).id
    }
  }

  const { data: updated, error: updateError } = await supabase
    .from('user_profiles')
    .update(fields)
    .eq('id', id)
    .select()
    .single()

  if (updateError) {
    return routeError('Benutzer konnte nicht aktualisiert werden.', updateError)
  }

  // Sync name changes back to linked consultant record
  const authId = (targetProfile as { auth_user_id?: string }).auth_user_id
  if (authId && (fields.first_name !== undefined || fields.last_name !== undefined || fields.display_name !== undefined)) {
    await syncProfileToConsultant(authId, {
      first_name:   fields.first_name,
      last_name:    fields.last_name,
      display_name: fields.display_name,
    })
  }

  await supabase.from('user_audit_log').insert({
    actor_id:       session.authUserId,
    target_user_id: id,
    action:         'user.updated',
    details:        fields,
  })

  return NextResponse.json({ profile: updated })
}

export async function DELETE(request: NextRequest) {
  const csrf = checkCsrf(request); if (csrf) return csrf
  const session = await getUserSession()
  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }
  if (session.role !== 'admin' && session.role !== 'masteradmin') {
    return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
  }

  const { searchParams } = new URL(request.url)
  const id = searchParams.get('id')
  if (!id) {
    return NextResponse.json({ error: 'Missing id' }, { status: 400 })
  }

  const supabase = await createClient()

  const { data: targetProfile } = await supabase
    .from('user_profiles')
    .select('role_id, roles ( code )')
    .eq('id', id)
    .single()

  if (!targetProfile) {
    return NextResponse.json({ error: 'User not found' }, { status: 404 })
  }

  const targetRoleCode = ((Array.isArray(targetProfile.roles) ? (targetProfile.roles as { code: string }[])[0] : (targetProfile.roles as { code: string } | null))?.code ?? 'readonly') as RoleCode
  if (!canManageUser(session, targetRoleCode)) {
    return NextResponse.json({ error: 'Insufficient privileges' }, { status: 403 })
  }

  const { error: deleteError } = await supabase
    .from('user_profiles')
    .update({
      deleted_at:     new Date().toISOString(),
      account_status: 'deactivated',
    })
    .eq('id', id)

  if (deleteError) {
    return routeError('Benutzer konnte nicht deaktiviert werden.', deleteError)
  }

  await supabase.from('user_audit_log').insert({
    actor_id:       session.authUserId,
    target_user_id: id,
    action:         'user.deleted',
    details:        {},
  })

  return NextResponse.json({ success: true })
}
