import { WebsiteRouteManager, RouteAccess } from './WebsiteRouteManager';
import { routes } from '../../../apps/website/lib/routing/RouteConfig';

/**
 * Expected HTTP status or behavior for a route.
 * - 'ok': 200 OK
 * - 'redirect': 3xx redirect (usually to login)
 * - 'forbidden': 403 Forbidden (or redirect to dashboard for wrong role)
 * - 'notFoundAllowed': 404 is an acceptable/expected outcome (e.g. for edge cases)
 * - 'errorRoute': The dedicated error pages themselves
 */
export type ExpectedStatus = 'ok' | 'redirect' | 'forbidden' | 'notFoundAllowed' | 'errorRoute';

/**
 * Roles that can access routes, used for scenario testing.
 */
export type ScenarioRole = 'unauth' | 'auth' | 'admin' | 'sponsor' | 'wrong-role';

/**
 * Expectations for a specific scenario/role.
 */
export interface ScenarioExpectation {
  expectedStatus: ExpectedStatus;
  expectedRedirectTo?: string | undefined;
  ssrMustContain?: Array<string | RegExp> | undefined;
  ssrMustNotContain?: Array<string | RegExp> | undefined;
}

/**
 * RouteContract defines the "Single Source of Truth" for how a website route
 * should behave during SSR and E2E testing.
 */
export interface RouteContract {
  /** The fully resolved path (e.g. /leagues/123 instead of /leagues/[id]) */
  path: string;
  /** The required access level for this route */
  accessLevel: RouteAccess;
  /** Baseline expectations (usually for the "intended" role or unauth) */
  expectedStatus: ExpectedStatus;
  /** If expectedStatus is 'redirect', where should it go? (pathname only) */
  expectedRedirectTo?: string | undefined;
  /** Strings or Regex that MUST be present in the SSR HTML */
  ssrMustContain?: Array<string | RegExp> | undefined;
  /** Strings or Regex that MUST NOT be present in the SSR HTML (e.g. error markers) */
  ssrMustNotContain?: Array<string | RegExp> | undefined;
  /** Minimum expected length of the HTML response body */
  minTextLength?: number | undefined;
  /** Per-role scenario expectations */
  scenarios: Partial<Record<ScenarioRole, ScenarioExpectation>>;
}

const DEFAULT_SSR_MUST_CONTAIN = ['<!DOCTYPE html>', '<body'];
const DEFAULT_SSR_MUST_NOT_CONTAIN = [
  '__NEXT_ERROR__',
  'Application error: a client-side exception has occurred',
];

/**
 * Generates the full list of route contracts by augmenting the base inventory
 * with expected behaviors and sanity checks.
 */
export function getWebsiteRouteContracts(): RouteContract[] {
  const manager = new WebsiteRouteManager();
  const inventory = manager.getWebsiteRouteInventory();

  // Per-route overrides for special cases where the group-based logic isn't enough
  const overrides: Record<string, Partial<RouteContract>> = {
    [routes.error.notFound]: {
      expectedStatus: 'notFoundAllowed',
    },
    [routes.error.serverError]: {
      expectedStatus: 'errorRoute',
    },
  };

  return inventory.map((def) => {
    const path = manager.resolvePathTemplate(def.pathTemplate, def.params);
    
    // Default augmentation based on access level
    let expectedStatus: ExpectedStatus = 'ok';
    let expectedRedirectTo: string | undefined = undefined;

    if (def.access !== 'public') {
      expectedStatus = 'redirect';
      // Most protected routes redirect to login when unauthenticated
      expectedRedirectTo = routes.auth.login;
    }

    // If the inventory explicitly allows 404 (e.g. for non-existent IDs in edge cases)
    if (def.allowNotFound) {
      expectedStatus = 'notFoundAllowed';
    }

    const contract: RouteContract = {
      path,
      accessLevel: def.access,
      expectedStatus,
      expectedRedirectTo,
      ssrMustContain: [...DEFAULT_SSR_MUST_CONTAIN],
      ssrMustNotContain: [...DEFAULT_SSR_MUST_NOT_CONTAIN],
      minTextLength: 1000, // Reasonable minimum for a Next.js page
      scenarios: {},
    };

    // Populate scenarios based on access level
    contract.scenarios.unauth = {
      expectedStatus: contract.expectedStatus,
      expectedRedirectTo: contract.expectedRedirectTo,
    };

    if (def.access === 'public') {
      contract.scenarios.auth = { expectedStatus: 'ok' };
      contract.scenarios.admin = { expectedStatus: 'ok' };
      contract.scenarios.sponsor = { expectedStatus: 'ok' };
    } else if (def.access === 'auth') {
      contract.scenarios.auth = { expectedStatus: 'ok' };
      contract.scenarios.admin = { expectedStatus: 'ok' };
      contract.scenarios.sponsor = { expectedStatus: 'ok' };
    } else if (def.access === 'admin') {
      contract.scenarios.auth = { expectedStatus: 'redirect', expectedRedirectTo: routes.protected.dashboard };
      contract.scenarios.admin = { expectedStatus: 'ok' };
      contract.scenarios.sponsor = { expectedStatus: 'redirect', expectedRedirectTo: routes.sponsor.dashboard };
      contract.scenarios['wrong-role'] = { expectedStatus: 'redirect', expectedRedirectTo: routes.protected.dashboard };
    } else if (def.access === 'sponsor') {
      contract.scenarios.auth = { expectedStatus: 'redirect', expectedRedirectTo: routes.protected.dashboard };
      contract.scenarios.admin = { expectedStatus: 'redirect', expectedRedirectTo: routes.admin.root };
      contract.scenarios.sponsor = { expectedStatus: 'ok' };
      contract.scenarios['wrong-role'] = { expectedStatus: 'redirect', expectedRedirectTo: routes.protected.dashboard };
    }

    // Apply per-route overrides (matching by template or resolved path)
    const override = overrides[def.pathTemplate] || overrides[path];
    if (override) {
      Object.assign(contract, override);
    }

    return contract;
  });
}
