import { NextRequest, NextResponse } from 'next/server';
import { writeFile } from 'node:fs/promises';
import { detectSetupState } from '@/lib/setup/state';
import { authenticateWizardRequest, SETUP_COOKIE } from '@/lib/setup/session';
import { configManager } from '@/lib/admin/config-manager';
import { setInitialAdminPassword } from '@/lib/admin/password';
import { clearSetupToken } from '@/lib/setup/token';
import { ensureConfigDir, getConfigPath } from '@/lib/admin/paths';
import { auditLog } from '@/lib/admin/audit';
import { logger } from '@/lib/logger';

export const dynamic = 'force-dynamic';

/**
 * POST /api/setup/finish
 *
 * Final wizard step. Validates that required config is in place, hashes the
 * admin password, marks setup complete, deletes the setup token (which
 * invalidates the wizard cookie), and optionally drops a `.config-locked`
 * marker so the operator remembers they intended to mount :ro.
 *
 * Body: { adminPassword: string, lockConfig?: boolean }
 */
export async function POST(request: NextRequest) {
  if (detectSetupState() !== 'bootstrap') {
    return NextResponse.json({ error: 'Setup is not active' }, { status: 404 });
  }
  if (!(await authenticateWizardRequest())) {
    return NextResponse.json({ error: 'Wizard session required' }, { status: 401 });
  }

  let body: { adminPassword?: unknown; lockConfig?: unknown };
  try {
    body = await request.json();
  } catch {
    return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
  }

  const adminPassword =
    typeof body?.adminPassword === 'string' ? body.adminPassword : '';
  if (adminPassword.length < 8) {
    return NextResponse.json(
      { error: 'Admin password must be at least 8 characters' },
      { status: 400 },
    );
  }

  const lockConfig = body?.lockConfig === true;

  // Validate required config is present.
  await configManager.ensureLoaded();
  const jmapUrl = configManager.get<string>('jmapServerUrl', '');
  if (!jmapUrl || typeof jmapUrl !== 'string') {
    return NextResponse.json(
      { error: 'JMAP server URL is required (run the Server step first)' },
      { status: 400 },
    );
  }

  try {
    // 1. Provision the admin account. An admin.json file may already exist
    //    from a previous ADMIN_PASSWORD env var or an aborted earlier wizard
    //    run while setupComplete is still false — accept the wizard's
    //    password as authoritative in that case. The finish route is gated
    //    by the bootstrap state + one-time setup token, so this is safe.
    const created = await setInitialAdminPassword(adminPassword, { allowOverwrite: true });
    if (!created) {
      return NextResponse.json(
        { error: 'Failed to write admin credentials' },
        { status: 500 },
      );
    }

    // 2. Persist setupComplete flag. After this, detectSetupState() flips
    //    to 'configured' and middleware starts 404'ing /setup paths.
    await configManager.markSetupComplete();

    // 3. Optional advisory lock marker.
    if (lockConfig) {
      await ensureConfigDir();
      await writeFile(
        getConfigPath('.config-locked'),
        new Date().toISOString(),
        'utf-8',
      );
    }

    // 4. Destroy the setup token. Any other browser holding the cookie is
    //    now unauthenticated.
    await clearSetupToken();

    await auditLog(
      'setup.finish',
      { lockConfig, jmapServerUrl: jmapUrl },
      request.headers.get('x-forwarded-for') ?? 'unknown',
    );

    const response = NextResponse.json({ ok: true, lockConfig });
    response.cookies.delete(SETUP_COOKIE);
    return response;
  } catch (error) {
    logger.error('Wizard finish failed', {
      error: error instanceof Error ? error.message : 'Unknown error',
    });
    return NextResponse.json(
      { error: 'Failed to finish setup', detail: error instanceof Error ? error.message : 'Unknown' },
      { status: 500 },
    );
  }
}
