/**
 * Next.js Proxy — runs before every request.
 * Renamed from `middleware.ts` in Next.js 16. Functionally identical.
 *
 * Responsibilities:
 *
 * 1. **Token exchange:** When the host CRM redirects to the plugin with
 *    `?token=<jwt>`, validate the token and set a session cookie. Strip the
 *    `token` query param via 302 redirect so it doesn't leak in the URL bar
 *    or referrer headers.
 *
 * 2. **Route gating:** Block unauthenticated requests to the UI (`/`,
 *    `/dashboard`, `/settings`). Tracking endpoints (`/api/track/*`) are
 *    intentionally public so emails can be opened/clicked by recipients who
 *    don't have plugin sessions.
 *
 * 3. **Bootstrap standalone** (`MAILFORGE_STANDALONE`): no CRM login — redirect
 *    once to `/api/auth/standalone/attach` to mint a session JWT.
 *
 * 4. **Native login** (`MAILFORGE_NATIVE_AUTH`): `/login` + password in DB; CRM
 *    data still via REST adapter (Settings). Mutually exclusive with bootstrap.
 *
 * The matcher at the bottom limits this proxy to UI routes — API routes
 * use the `withAuth` HOF from `@/core/auth` for finer-grained control.
 */

import { NextRequest, NextResponse } from 'next/server';
import { verifyJwt } from '@/core/auth/jwt';
import { setSessionCookie, extractToken } from '@/core/auth/cookies';
import {
  isBootstrapStandaloneEnabled,
  isNativeAuthEnabled,
  isEmbeddedProductMode,
} from '@/core/auth/standalone';

const PUBLIC_PATH_PREFIXES = [
  '/api/track/',     // open pixel, click wrapper, conversion pixel, webhooks
  '/api/m365-diagnose', // diagnose endpoint (used by host CRM admin tools)
  '/_next/',
  '/favicon',
  '/assets/',
];

function isPublic(pathname: string): boolean {
  if (
    isBootstrapStandaloneEnabled() &&
    pathname.startsWith('/api/auth/standalone/attach')
  ) {
    return true;
  }
  if (isNativeAuthEnabled()) {
    if (pathname === '/login') return true;
    if (pathname.startsWith('/api/auth/native/')) return true;
  }
  return PUBLIC_PATH_PREFIXES.some((prefix) => pathname.startsWith(prefix));
}

export async function proxy(req: NextRequest): Promise<NextResponse | undefined> {
  const { pathname, searchParams } = req.nextUrl;

  if (isPublic(pathname)) return undefined;

  // Step 1: token exchange — host CRM redirects to /?token=<jwt>
  const handoffToken = searchParams.get('token');
  if (handoffToken) {
    try {
      await verifyJwt(handoffToken);
    } catch (err) {
      console.warn('[mf-mw] handoff JWT verify failed:', err instanceof Error ? err.message : String(err));
      const url = req.nextUrl.clone();
      url.searchParams.delete('token');
      return NextResponse.redirect(url);
    }

    // Token is valid — clear query param, set cookie, redirect to same path.
    const url = req.nextUrl.clone();
    url.searchParams.delete('token');
    const res = NextResponse.redirect(url);
    setSessionCookie({ response: res, token: handoffToken });
    return res;
  }

  // Step 2: route gating — accept Bearer header OR session cookie
  const existingToken = extractToken(req);
  if (!existingToken) {
    if (isNativeAuthEnabled()) {
      if (req.method === 'GET' && !pathname.startsWith('/api/')) {
        const login = new URL('/login', req.url);
        const dest = pathname + (req.nextUrl.search || '');
        login.searchParams.set('next', dest || '/');
        return NextResponse.redirect(login);
      }
      return new NextResponse(
        JSON.stringify({
          error: 'Authentication required. Sign in at /login.',
          errorKind: 'no-token',
        }),
        { status: 401, headers: { 'Content-Type': 'application/json' } },
      );
    }
    if (isBootstrapStandaloneEnabled()) {
      const attach = new URL('/api/auth/standalone/attach', req.url);
      const dest = pathname + (req.nextUrl.search || '');
      attach.searchParams.set('next', dest || '/');
      return NextResponse.redirect(attach);
    }
    return new NextResponse(
      JSON.stringify({
        error: isEmbeddedProductMode()
          ? 'Authentication required. Open this app from your CRM, enable MAILFORGE_STANDALONE, or use native login.'
          : 'Authentication required.',
      }),
      { status: 401, headers: { 'Content-Type': 'application/json' } },
    );
  }

  try {
    await verifyJwt(existingToken);
  } catch (err) {
    console.warn('[mf-mw] existing token invalid:', err instanceof Error ? err.message : String(err));
    return new NextResponse(
      JSON.stringify({
        error:
          isNativeAuthEnabled() || isBootstrapStandaloneEnabled()
            ? 'Session expired. Reload the page to sign in again.'
            : isEmbeddedProductMode()
              ? 'Session expired. Open this app from your CRM again.'
              : 'Session expired. Reload the page.',
        errorKind: 'expired',
      }),
      { status: 401, headers: { 'Content-Type': 'application/json' } },
    );
  }

  return undefined;
}

export const config = {
  // Apply proxy to all routes except:
  //   - /_next/* (static assets, prefetch)
  //   - public api endpoints (tracking, etc) handled via early-return above
  //   - actual file requests (.png, .ico, .map etc)
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
