import * as fs from 'fs';
import * as path from 'path';

export type RouteAccess = 'public' | 'auth' | 'admin' | 'sponsor';

export type RouteParams = Record<string, string>;

export type WebsiteRouteDefinition = {
  pathTemplate: string;
  params?: RouteParams;
  access: RouteAccess;
  expectedPathTemplate?: string;
  allowNotFound?: boolean;
};

function walkDir(rootDir: string): string[] {
  const entries = fs.readdirSync(rootDir, { withFileTypes: true });
  const results: string[] = [];

  for (const entry of entries) {
    if (entry.name.startsWith('.')) continue;

    const fullPath = path.join(rootDir, entry.name);

    if (entry.isDirectory()) {
      results.push(...walkDir(fullPath));
      continue;
    }

    results.push(fullPath);
  }

  return results;
}

function toPathTemplate(appDir: string, pageFilePath: string): string {
  const rel = path.relative(appDir, pageFilePath);
  const segments = rel.split(path.sep);

  // drop trailing "page.tsx"
  segments.pop();

  // root page.tsx
  if (segments.length === 0) return '/';

  return `/${segments.join('/')}`;
}

export function listNextAppPageTemplates(appDir?: string): string[] {
  const resolvedAppDir = appDir ?? path.join(process.cwd(), 'apps', 'website', 'app');

  const files = walkDir(resolvedAppDir);
  const pages = files.filter((f) => path.basename(f) === 'page.tsx');

  return pages.map((pagePath) => toPathTemplate(resolvedAppDir, pagePath));
}

export function resolvePathTemplate(pathTemplate: string, params: RouteParams = {}): string {
  return pathTemplate.replace(/\[([^\]]+)\]/g, (_match, key: string) => {
    const replacement = params[key];
    if (!replacement) {
      throw new Error(`Missing route param "${key}" for template "${pathTemplate}"`);
    }
    return replacement;
  });
}

// Default IDs used to resolve dynamic routes in smoke tests.
// These values must be supported by the docker mock API in docker-compose.test.yml.
const LEAGUE_ID = 'league-1';
const DRIVER_ID = 'driver-1';
const TEAM_ID = 'team-1';
const RACE_ID = 'race-1';
const PROTEST_ID = 'protest-1';

const ROUTE_META: Record<string, Omit<WebsiteRouteDefinition, 'pathTemplate'>> = {
  '/': { access: 'public' },

  '/404': { access: 'public' },
  '/500': { access: 'public' },

  '/auth/iracing': { access: 'public' },
  '/auth/login': { access: 'public' },
  '/auth/signup': { access: 'public' },

  '/dashboard': { access: 'auth' },

  '/drivers': { access: 'public' },
  '/drivers/[id]': { access: 'public', params: { id: DRIVER_ID } },

  '/leaderboards': { access: 'public' },
  '/leaderboards/drivers': { access: 'public' },

  '/leagues': { access: 'public' },
  '/leagues/create': { access: 'auth' },
  '/leagues/[id]': { access: 'public', params: { id: LEAGUE_ID } },
  '/leagues/[id]/roster/admin': { access: 'admin', params: { id: LEAGUE_ID } },
  '/leagues/[id]/rulebook': { access: 'public', params: { id: LEAGUE_ID } },
  '/leagues/[id]/schedule': { access: 'public', params: { id: LEAGUE_ID } },
  '/leagues/[id]/schedule/admin': { access: 'admin', params: { id: LEAGUE_ID } },
  '/leagues/[id]/settings': { access: 'admin', params: { id: LEAGUE_ID } },
  '/leagues/[id]/sponsorships': { access: 'admin', params: { id: LEAGUE_ID } },
  '/leagues/[id]/standings': { access: 'public', params: { id: LEAGUE_ID } },
  '/leagues/[id]/stewarding': { access: 'admin', params: { id: LEAGUE_ID } },
  '/leagues/[id]/stewarding/protests/[protestId]': {
    access: 'admin',
    params: { id: LEAGUE_ID, protestId: PROTEST_ID },
  },
  '/leagues/[id]/wallet': { access: 'admin', params: { id: LEAGUE_ID } },

  '/onboarding': { access: 'auth' },

  '/profile': { access: 'auth' },
  '/profile/leagues': { access: 'auth' },
  '/profile/liveries': { access: 'auth' },
  '/profile/liveries/upload': { access: 'auth' },
  '/profile/settings': { access: 'auth' },
  '/profile/sponsorship-requests': { access: 'auth' },

  '/races': { access: 'public' },
  '/races/all': { access: 'public' },
  '/races/[id]': { access: 'public', params: { id: RACE_ID } },
  '/races/[id]/results': { access: 'public', params: { id: RACE_ID } },
  '/races/[id]/stewarding': { access: 'admin', params: { id: RACE_ID } },

  '/sponsor': { access: 'sponsor', expectedPathTemplate: '/sponsor/dashboard' },
  '/sponsor/billing': { access: 'sponsor' },
  '/sponsor/campaigns': { access: 'sponsor' },
  '/sponsor/dashboard': { access: 'sponsor' },
  '/sponsor/leagues': { access: 'sponsor' },
  '/sponsor/leagues/[id]': { access: 'sponsor', params: { id: LEAGUE_ID } },
  '/sponsor/settings': { access: 'sponsor' },
  '/sponsor/signup': { access: 'public' },

  '/teams': { access: 'public' },
  '/teams/leaderboard': { access: 'public' },
  '/teams/[id]': { access: 'public', params: { id: TEAM_ID } },
};

export function getWebsiteRouteInventory(): WebsiteRouteDefinition[] {
  const discovered = listNextAppPageTemplates();

  const missingMeta = discovered.filter((template) => !ROUTE_META[template]);
  if (missingMeta.length > 0) {
    throw new Error(
      `Missing ROUTE_META entries for discovered pages:\n${missingMeta
        .slice()
        .sort()
        .map((t) => `- ${t}`)
        .join('\n')}`,
    );
  }

  const extraMeta = Object.keys(ROUTE_META).filter((template) => !discovered.includes(template));
  if (extraMeta.length > 0) {
    throw new Error(
      `ROUTE_META contains templates that are not present as page.tsx routes:\n${extraMeta
        .slice()
        .sort()
        .map((t) => `- ${t}`)
        .join('\n')}`,
    );
  }

  return discovered
    .slice()
    .sort()
    .map((pathTemplate) => ({ pathTemplate, ...ROUTE_META[pathTemplate] }));
}

export function getWebsiteParamEdgeCases(): WebsiteRouteDefinition[] {
  return [
    { pathTemplate: '/races/[id]', params: { id: 'does-not-exist' }, access: 'public', allowNotFound: true },
    { pathTemplate: '/leagues/[id]', params: { id: 'does-not-exist' }, access: 'public', allowNotFound: true },
  ];
}

export function getWebsiteFaultInjectionRoutes(): WebsiteRouteDefinition[] {
  return [
    { pathTemplate: '/leagues/[id]', params: { id: LEAGUE_ID }, access: 'public' },
    { pathTemplate: '/leagues/[id]/schedule/admin', params: { id: LEAGUE_ID }, access: 'admin' },
    { pathTemplate: '/sponsor/dashboard', access: 'sponsor' },
    { pathTemplate: '/races/[id]', params: { id: RACE_ID }, access: 'public' },
  ];
}

export function getWebsiteAuthDriftRoutes(): WebsiteRouteDefinition[] {
  return [{ pathTemplate: '/sponsor/dashboard', access: 'sponsor' }];
}