import fs from "node:fs/promises";
import path from "node:path";
import { describe, it, expect } from "vitest";
import { SUPPORTED_POS, SUPPORTED_LANGUAGE_CODES } from "@lila/shared";
import type { SupportedPos, SupportedLanguageCode } from "@lila/shared";

// ── Types ─────────────────────────────────────────────────────────────────────

type OmwRecord = {
  source_id: string;
  pos: SupportedPos;
  translations: Partial<Record<SupportedLanguageCode, string[]>>;
  glosses: Partial<Record<SupportedLanguageCode, string[]>>;
  examples: Partial<Record<SupportedLanguageCode, string[]>>;
};

// ── Paths ─────────────────────────────────────────────────────────────────────

const OMW_PATH = path.resolve("stage-1-extract/output/omw.json");

// ── Helpers ───────────────────────────────────────────────────────────────────

function isValidSourceId(id: string): boolean {
  return /^ili:i\d+$/.test(id);
}

// ── Tests ─────────────────────────────────────────────────────────────────────

describe("stage 1 — omw.json validation", () => {
  let records: OmwRecord[];

  it("file exists and is valid JSON", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    records = JSON.parse(raw) as OmwRecord[];
    expect(records).toBeDefined();
  });

  it("is a non-empty array", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    records = JSON.parse(raw) as OmwRecord[];
    expect(Array.isArray(records)).toBe(true);
    expect(records.length).toBeGreaterThan(0);
  });

  it("every record has required fields", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    records = JSON.parse(raw) as OmwRecord[];

    const errors: string[] = [];

    for (const record of records) {
      if (!record.source_id) {
        errors.push(`missing source_id`);
        continue;
      }
      if (!record.pos) errors.push(`${record.source_id}: missing pos`);
      if (!record.translations)
        errors.push(`${record.source_id}: missing translations`);
      if (!record.glosses) errors.push(`${record.source_id}: missing glosses`);
      if (!record.examples)
        errors.push(`${record.source_id}: missing examples`);
    }

    expect(errors, `\n${errors.join("\n")}`).toHaveLength(0);
  });

  it("every source_id matches ili:i{number} pattern", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    records = JSON.parse(raw) as OmwRecord[];

    const errors: string[] = [];

    for (const record of records) {
      if (!isValidSourceId(record.source_id)) {
        errors.push(`invalid source_id: ${record.source_id}`);
      }
    }

    expect(errors, `\n${errors.join("\n")}`).toHaveLength(0);
  });

  it("every source_id is unique", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    records = JSON.parse(raw) as OmwRecord[];

    const seen = new Set<string>();
    const errors: string[] = [];

    for (const record of records) {
      if (seen.has(record.source_id)) {
        errors.push(`duplicate source_id: ${record.source_id}`);
      }
      seen.add(record.source_id);
    }

    expect(errors, `\n${errors.join("\n")}`).toHaveLength(0);
  });

  it("every pos is a valid supported value", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    records = JSON.parse(raw) as OmwRecord[];

    const errors: string[] = [];
    const validPos = new Set(SUPPORTED_POS);

    for (const record of records) {
      if (!validPos.has(record.pos)) {
        errors.push(`${record.source_id}: invalid pos "${record.pos}"`);
      }
    }

    expect(errors, `\n${errors.join("\n")}`).toHaveLength(0);
  });

  it("every record has at least one translation in at least one language", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    records = JSON.parse(raw) as OmwRecord[];

    const errors: string[] = [];
    const validLangs = new Set(SUPPORTED_LANGUAGE_CODES);

    for (const record of records) {
      const langs = Object.keys(record.translations) as SupportedLanguageCode[];

      if (langs.length === 0) {
        errors.push(`${record.source_id}: no translations`);
        continue;
      }

      for (const lang of langs) {
        if (!validLangs.has(lang)) {
          errors.push(`${record.source_id}: unsupported language "${lang}"`);
        }
        const words = record.translations[lang] ?? [];
        if (words.length === 0) {
          errors.push(`${record.source_id}: empty translations for "${lang}"`);
        }
      }
    }

    expect(errors, `\n${errors.join("\n")}`).toHaveLength(0);
  });

  it("no duplicate translations within a single synset and language", async () => {
    const raw = await fs.readFile(OMW_PATH, "utf-8");
    const records = JSON.parse(raw) as OmwRecord[];

    const errors: string[] = [];

    for (const record of records) {
      for (const [lang, words] of Object.entries(record.translations)) {
        const seen = new Set<string>();
        for (const word of words) {
          if (seen.has(word)) {
            errors.push(
              `${record.source_id} (${lang}): duplicate translation "${word}"`,
            );
          }
          seen.add(word);
        }
      }
    }

    expect(errors, `\n${errors.join("\n")}`).toHaveLength(0);
  });
});
