export interface StoredRedirect {
  from: string;
  to: string;
}

export interface RedirectRepository {
  findBySourcePath(pathname: string): StoredRedirect | undefined;
}

export class EnvRedirectRepository implements RedirectRepository {
  private readonly redirects: readonly StoredRedirect[];

  constructor(rawJson = process.env.REDIRECTS_JSON) {
    this.redirects = parseRedirects(rawJson);
  }

  findBySourcePath(pathname: string): StoredRedirect | undefined {
    return this.redirects.find((redirect) => redirect.from === normalizePath(pathname));
  }
}

export function parseRedirects(rawJson: string | undefined): readonly StoredRedirect[] {
  if (rawJson === undefined || rawJson.trim() === '') return [];

  try {
    const parsed = JSON.parse(rawJson) as unknown;
    if (!Array.isArray(parsed)) return [];
    return parsed.flatMap((entry) => {
      if (!isRedirectEntry(entry)) return [];
      const from = normalizePath(entry.from);
      const to = normalizePath(entry.to);
      if (!isSafeLocalPath(from) || !isSafeLocalPath(to) || from === to) return [];
      return [{ from, to }];
    });
  } catch {
    return [];
  }
}

function isRedirectEntry(value: unknown): value is StoredRedirect {
  return (
    typeof value === 'object' &&
    value !== null &&
    'from' in value &&
    'to' in value &&
    typeof value.from === 'string' &&
    typeof value.to === 'string'
  );
}

function isSafeLocalPath(pathname: string): boolean {
  return pathname.startsWith('/') && !pathname.startsWith('//') && !pathname.includes('://');
}

function normalizePath(pathname: string): string {
  if (pathname.length > 1) return pathname.replace(/\/+$/, '');
  return pathname;
}
