import {
  MetricsFile, SampleAnalysis, ErrorAnalysisFile,
  ModelInfo, MODEL_REGISTRY, LANGUAGES,
  isOldSchema, migrateLanguageMetrics, OldLanguageMetrics,
  AggregateMetrics, MacroAvgMetrics,
} from "./types";

const BENCHMARK_API = process.env.BENCHMARK_API_URL || "http://localhost:8787";

interface NginxDirEntry {
  name: string;
  type: "directory" | "file";
  mtime: string;
}

async function fetchJson<T>(url: string): Promise<T | null> {
  try {
    const res = await fetch(url, { cache: "no-store" });
    if (!res.ok) return null;
    return (await res.json()) as T;
  } catch {
    return null;
  }
}

// Migrate old-schema metrics.json into new shape
function migrateMetricsFile(raw: Record<string, unknown>): MetricsFile {
  // Check if this is old schema by inspecting a language key
  const firstLang = LANGUAGES.find((l) => l in raw);
  if (!firstLang || !isOldSchema(raw[firstLang] as Record<string, unknown>)) {
    return raw as unknown as MetricsFile;
  }

  const migrated: Record<string, unknown> = {};

  for (const lang of LANGUAGES) {
    if (raw[lang]) {
      migrated[lang] = migrateLanguageMetrics(raw[lang] as OldLanguageMetrics);
    }
  }

  // Migrate __overall__
  const oldOverall = raw.__overall__ as { n_samples: number; wer: number; cer: number; wer_normalized: number; cer_normalized: number } | undefined;
  if (oldOverall) {
    migrated.__overall__ = {
      n_samples: oldOverall.n_samples,
      wer_raw: oldOverall.wer,
      wer_norm: oldOverall.wer_normalized,
      wer_numcanon: oldOverall.wer_normalized,
      mer: oldOverall.cer_normalized,
      cer_norm: oldOverall.cer_normalized,
    } as AggregateMetrics;
  }

  // Migrate __macro_avg__
  const oldMacro = raw.__macro_avg__ as { n_languages: number; wer: number; cer: number; wer_normalized: number; cer_normalized: number } | undefined;
  if (oldMacro) {
    migrated.__macro_avg__ = {
      n_languages: oldMacro.n_languages,
      wer_raw: oldMacro.wer,
      wer_norm: oldMacro.wer_normalized,
      wer_numcanon: oldMacro.wer_normalized,
      mer: oldMacro.cer_normalized,
      cer_norm: oldMacro.cer_normalized,
    } as MacroAvgMetrics;
  }

  migrated.__meta__ = raw.__meta__;

  return migrated as unknown as MetricsFile;
}

// Migrate old sample_analysis entries
function migrateSamples(raw: Record<string, unknown>[]): SampleAnalysis[] {
  return raw.map((s) => {
    const sample = s as Record<string, string>;
    return {
      id: sample.id,
      language: sample.language,
      reference: sample.reference,
      hypothesis: sample.hypothesis,
      ref_norm: sample.ref_norm || sample.ref_normalized,
      hyp_norm: sample.hyp_norm || sample.hyp_normalized,
      detected_language: sample.detected_language,
    } as SampleAnalysis;
  });
}

export async function getModels(): Promise<ModelInfo[]> {
  const models: ModelInfo[] = [];

  for (const [modelId, displayName] of Object.entries(MODEL_REGISTRY)) {
    const entries = await fetchJson<NginxDirEntry[]>(
      `${BENCHMARK_API}/api/benchmarks/${modelId}/`
    );

    if (!entries) {
      models.push({ id: modelId, displayName, checkpoints: [] });
      continue;
    }

    const dirs = entries
      .filter((e) => e.type === "directory")
      .map((e) => e.name);

    const checkpoints: string[] = [];
    for (const dir of dirs) {
      const metrics = await fetchJson<Record<string, unknown>>(
        `${BENCHMARK_API}/api/benchmarks/${modelId}/${dir}/metrics.json`
      );
      if (metrics) checkpoints.push(dir);
    }

    checkpoints.sort((a, b) => {
      const numA = parseInt(a.replace(/\D/g, "")) || 0;
      const numB = parseInt(b.replace(/\D/g, "")) || 0;
      return numA - numB;
    });

    models.push({ id: modelId, displayName, checkpoints });
  }

  return models;
}

export async function getMetrics(modelId: string, checkpoint: string): Promise<MetricsFile | null> {
  const raw = await fetchJson<Record<string, unknown>>(
    `${BENCHMARK_API}/api/benchmarks/${modelId}/${checkpoint}/metrics.json`
  );
  if (!raw) return null;
  return migrateMetricsFile(raw);
}

export async function getSamples(modelId: string, checkpoint: string): Promise<SampleAnalysis[] | null> {
  const raw = await fetchJson<Record<string, unknown>[]>(
    `${BENCHMARK_API}/api/benchmarks/${modelId}/${checkpoint}/sample_analysis.json`
  );
  if (!raw) return null;
  return migrateSamples(raw);
}

export async function getErrorAnalysis(modelId: string, checkpoint: string): Promise<ErrorAnalysisFile | null> {
  return fetchJson<ErrorAnalysisFile>(
    `${BENCHMARK_API}/api/benchmarks/${modelId}/${checkpoint}/error_analysis.json`
  );
}
