import { NextRequest, NextResponse } from 'next/server';
import { requireAuth } from '@/lib/auth-utils';
import { postgresClient } from '@/lib/services/postgres-client';

// GET  /api/me/timezone   -> { timezone: string, source: 'user' | 'default' }
// PUT  /api/me/timezone   -> body { timezone: string } -> { timezone: string }
//
// TZ-03. Authentication: requireAuth(). The PUT handler updates ONLY the
// calling user's row — there is no `userId` query param or body field. The
// write target is always `session.user.id`.
//
// Validation: the input timezone must appear in
// `Intl.supportedValuesOf('timeZone')`. Anything else is rejected with 400
// before touching the database.

function getDefaultTimezone(): string {
  return process.env.DEFAULT_TIMEZONE || 'UTC';
}

// Node's Intl.supportedValuesOf('timeZone') returns canonical IANA zones only —
// it omits 'UTC', 'Etc/UTC', 'GMT', and the entire Etc/* alias namespace, even
// though those are valid for Postgres AT TIME ZONE and JS Date methods. The
// migration default is 'UTC', so the validator must accept it explicitly.
const EXTRA_ALLOWED_TIMEZONES = new Set(['UTC', 'Etc/UTC', 'GMT', 'Etc/GMT']);

function isValidIanaTimezone(tz: unknown): tz is string {
  if (typeof tz !== 'string' || tz.length === 0 || tz.length > 64) return false;
  if (EXTRA_ALLOWED_TIMEZONES.has(tz)) return true;
  try {
    const zones = Intl.supportedValuesOf('timeZone');
    return zones.includes(tz);
  } catch {
    return false;
  }
}

export async function GET(): Promise<NextResponse> {
  const { session, error } = await requireAuth();
  if (error) return error;

  try {
    const result = await postgresClient.query<{ timezone: string | null }>(
      'SELECT timezone FROM "user" WHERE id = $1',
      [session!.user.id],
    );
    const stored = result.rows[0]?.timezone;
    const fallback = getDefaultTimezone();
    const timezone = stored && stored.length > 0 ? stored : fallback;
    const source: 'user' | 'default' =
      stored && stored.length > 0 && stored !== fallback ? 'user' : 'default';
    return NextResponse.json({ timezone, source });
  } catch (e) {
    console.error('GET /api/me/timezone failed:', e);
    return NextResponse.json(
      { error: 'Failed to read timezone', message: e instanceof Error ? e.message : 'unknown' },
      { status: 500 },
    );
  }
}

export async function PUT(request: NextRequest): Promise<NextResponse> {
  const { session, error } = await requireAuth();
  if (error) return error;

  let body: unknown;
  try {
    body = await request.json();
  } catch {
    return NextResponse.json(
      { error: 'Invalid JSON', message: 'Request body must be JSON' },
      { status: 400 },
    );
  }

  const candidate =
    body && typeof body === 'object' && 'timezone' in body
      ? (body as { timezone: unknown }).timezone
      : undefined;

  if (!isValidIanaTimezone(candidate)) {
    return NextResponse.json(
      {
        error: 'Invalid timezone',
        message: "timezone must be an IANA zone present in Intl.supportedValuesOf('timeZone')",
      },
      { status: 400 },
    );
  }

  try {
    // Authoritative write target: session.user.id. NO userId from body.
    const result = await postgresClient.query<{ timezone: string }>(
      'UPDATE "user" SET timezone = $1, "updatedAt" = NOW() WHERE id = $2 RETURNING timezone',
      [candidate, session!.user.id],
    );
    if (result.rowCount === 0) {
      return NextResponse.json(
        { error: 'User not found', message: 'No user row matched the session' },
        { status: 404 },
      );
    }
    return NextResponse.json({ timezone: result.rows[0].timezone });
  } catch (e) {
    console.error('PUT /api/me/timezone failed:', e);
    return NextResponse.json(
      { error: 'Failed to update timezone', message: e instanceof Error ? e.message : 'unknown' },
      { status: 500 },
    );
  }
}
