import { NextResponse, type NextRequest } from 'next/server';
import { jwtVerify } from 'jose';
import { JWT_SECRET } from '@server/auth/jwt-secret';
import { evaluateGateForSession } from '@server/services/billing/gate.service';
import type { SessionPayload } from '@server/auth';

// NOTE: We deliberately do NOT import the shared Pino logger here. The
// middleware bundle is built with a different module resolution path
// than the rest of the server, and pulling in `pino` (and especially
// `pino-pretty`'s worker entry) caused "Module not found: Can't resolve
// 'pino'" build warnings. A single inline `console.warn` is plenty for
// the one fail-open path below.

export const config = {
  runtime: 'nodejs',
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|wav|mp3)$).*)',
  ],
};

const COOKIE_NAME = 'restroagent_session';
const SUB_GATE_COOKIE = 'sub_gate';
const SUB_GATE_TTL_SECONDS = 300;

const PUBLIC_PATHS = [
  '/login',
  '/register',
  '/forgot-password',
  '/reset-password',
  '/demo-setup',
  '/demo',
  '/home-landing',
  '/about',
  '/blog',
  '/contact',
  '/faq',
  '/pricing',
  '/terms',
  '/privacy',
  '/cookie',
  '/refund',
  '/api/',
  '/auth/',
  '/r/',
];

const TRIAL_GATE_EXEMPT = ['/billing', '/onboarding', '/sign-out', '/logout'];

function isPublicPath(pathname: string): boolean {
  if (pathname === '/') return true;
  return PUBLIC_PATHS.some((p) => pathname.startsWith(p));
}

function isTrialGateExempt(pathname: string): boolean {
  return TRIAL_GATE_EXEMPT.some((p) => pathname.startsWith(p));
}

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  if (isPublicPath(pathname)) {
    return NextResponse.next();
  }

  const token = request.cookies.get(COOKIE_NAME)?.value;
  if (!token) {
    const url = request.nextUrl.clone();
    url.pathname = '/login';
    return NextResponse.redirect(url);
  }

  let session: SessionPayload | null = null;
  try {
    const { payload } = await jwtVerify(token, JWT_SECRET);
    session = payload as unknown as SessionPayload;
  } catch {
    const url = request.nextUrl.clone();
    url.pathname = '/login';
    return NextResponse.redirect(url);
  }

  const role = session?.role;
  const isAdmin = role === 'superadmin' || role === 'support';

  if (!isAdmin && !isTrialGateExempt(pathname)) {
    const cachedGate = request.cookies.get(SUB_GATE_COOKIE)?.value;

    if (cachedGate) {
      if (cachedGate === 'expired' || cachedGate === 'inactive') {
        const url = request.nextUrl.clone();
        url.pathname = '/billing';
        url.search = '?reason=trial_expired';
        return NextResponse.redirect(url);
      }
      return NextResponse.next();
    }

    // No cached gate — evaluate inline (no internal HTTP fetch). Fail-open
    // on unexpected errors: only an *explicit* 'expired' or 'inactive' from
    // the helper should ever redirect/cache. Any thrown error (DB blip,
    // timeout, etc.) means we can't prove the trial ended, so we let the
    // request through without caching.
    let gateValue;
    try {
      gateValue = await evaluateGateForSession(session);
    } catch (err) {
      console.warn('[middleware.subgate] sub gate evaluation failed, failing open', {
        err: err instanceof Error ? err.message : String(err),
        path: pathname,
      });
      return NextResponse.next();
    }

    if (gateValue === 'unauthenticated') {
      const url = request.nextUrl.clone();
      url.pathname = '/login';
      return NextResponse.redirect(url);
    }

    const response =
      gateValue === 'expired' || gateValue === 'inactive'
        ? (() => {
            const url = request.nextUrl.clone();
            url.pathname = '/billing';
            url.search = '?reason=trial_expired';
            return NextResponse.redirect(url);
          })()
        : NextResponse.next();
    response.cookies.set(SUB_GATE_COOKIE, gateValue, {
      httpOnly: true,
      sameSite: 'lax',
      path: '/',
      maxAge: SUB_GATE_TTL_SECONDS,
      secure: process.env.NODE_ENV === 'production',
    });
    return response;
  }

  return NextResponse.next();
}
