import {NextResponse} from 'next/server';
import type {NextRequest} from 'next/server';
import {
  SESSION_COOKIE,
  verifySessionToken,
} from '@/features/auth/services/sessionToken';

/**
 * The authentication gate. `proxy.ts`, not `middleware.ts` — the middleware
 * file convention is deprecated in Next 16 and renamed; same semantics.
 *
 * This runs BEFORE any protected route renders, which is what makes the
 * workspace genuinely unreachable rather than merely hidden. A client-side
 * guard alone can be defeated by disabling JS or by racing the redirect; a
 * request that never gets a page back cannot be.
 *
 * Three rules, in order:
 *
 *   1. `/`                     → /dashboard when signed in, else /login
 *   2. a protected path        → /login?next=… when there is no valid session
 *   3. /login while signed in  → /dashboard
 *
 * Rule 3 is what makes /login a GuestGuard-ed route: coming back to it with a
 * live session should not offer to sign you in again.
 *
 * The session is verified, not merely detected. A cookie whose signature fails
 * or whose `exp` has passed is treated as absent, so a tampered or stale
 * cookie cannot buy access to a single render.
 */

const LOGIN_PATH = '/login';
const HOME_PATH = '/dashboard';

/**
 * Everything under these prefixes requires a session. Listed explicitly rather
 * than derived by exclusion: a new public page should have to opt IN to being
 * public, but a new private one must not have to remember to opt in to being
 * protected — and the matcher below already excludes public paths, so anything
 * unrecognised falls through to protected by default.
 */
const PUBLIC_PATHS = new Set([LOGIN_PATH]);

export function proxy(request: NextRequest): NextResponse {
  const {pathname, search} = request.nextUrl;

  const session = verifySessionToken(
    request.cookies.get(SESSION_COOKIE)?.value,
  );
  const isAuthenticated = session !== null;

  if (pathname === '/') {
    return NextResponse.redirect(
      new URL(isAuthenticated ? HOME_PATH : LOGIN_PATH, request.url),
    );
  }

  if (PUBLIC_PATHS.has(pathname)) {
    if (isAuthenticated) {
      return NextResponse.redirect(new URL(HOME_PATH, request.url));
    }
    return NextResponse.next();
  }

  if (!isAuthenticated) {
    /**
     * An API caller gets a 401, never a redirect.
     *
     * Measured before this branch existed: `GET /api/staff` without a session
     * answered 307 → /login, so `fetch()` followed it and handed the client an
     * HTML page where JSON was expected. Every data hook would have reported
     * "Unexpected response" instead of "signed out", which is the difference
     * between a session that expired and a server that broke.
     */
    if (pathname.startsWith('/api/')) {
      return NextResponse.json(
        {error: {code: 'unauthorized', message: 'Sign in to continue.'}},
        {status: 401, headers: {'cache-control': 'no-store'}},
      );
    }

    const target = new URL(LOGIN_PATH, request.url);
    // Where they were headed, so the login form can finish the journey rather
    // than dumping every user on the dashboard. Path only — never an absolute
    // URL, which would make this an open redirect.
    if (pathname !== HOME_PATH) {
      target.searchParams.set('next', `${pathname}${search}`);
    }
    return NextResponse.redirect(target);
  }

  return NextResponse.next();
}

export const config = {
  /**
   * Everything except Next's own assets, the auth endpoints and static files.
   *
   * /api/auth is excluded because login has to be callable BY a signed-out
   * user — gating it would make signing in impossible. The rest of /api is
   * intentionally included: an unauthenticated fetch for dashboard data should
   * not succeed just because it skipped the page.
   *
   * `.*\\.[\\w]+$` excludes anything that looks like a file, and that pattern
   * is load-bearing rather than tidy-minded. next/image optimises a local
   * asset by fetching it back from this same server, and that internal request
   * carries no cookies — so with the logo unlisted, the proxy redirected the
   * optimiser to /login, it received HTML where a PNG should have been, and
   * the brand lockup rendered as a broken image on the sign-in screen.
   * Matching by extension means a new asset can never reintroduce that.
   *
   * Route paths carry no extension, so nothing protected is excluded by it.
   */
  matcher: ['/((?!api/auth|_next/static|_next/image|.*\\.[\\w]+$).*)'],
};
