import { describe, it, expect } from "vitest";
import type { Difficulty, MathProblem } from "../types";
import { generateFractionBodmas, generateSignedFraction } from "./fraction-problems";
import { generateDefineRatio, generateFractionsAndRatios } from "./ratio-problems";
import { generateIntegerOrderCompare, generateIntegerAddSubtractMixed } from "./integer-problems";
import { generateQuaternaryToDenary } from "./number-system-problems";

function assertWellFormed(p: MathProblem) {
  expect(typeof p.prompt).toBe("string");
  expect(p.prompt.length).toBeGreaterThan(0);
  expect(Array.isArray(p.hints)).toBe(true);
  expect(Array.isArray(p.steps)).toBe(true);

  const a = p.answer;
  if (a.kind === "integer") {
    expect(Number.isFinite(a.value)).toBe(true);
  } else if (a.kind === "fraction") {
    expect(Number.isInteger(a.numerator)).toBe(true);
    expect(Number.isInteger(a.denominator)).toBe(true);
    expect(a.denominator).not.toBe(0);
  } else if (a.kind === "ratio") {
    expect(a.parts.length).toBeGreaterThan(0);
    a.parts.forEach((v) => expect(Number.isFinite(v)).toBe(true));
  }
}

const NEW_GENERATORS: Record<string, (d: Difficulty) => MathProblem> = {
  "fraction BODMAS": generateFractionBodmas,
  "signed fraction": generateSignedFraction,
  "define ratio": generateDefineRatio,
  "fractions and ratios": generateFractionsAndRatios,
  "integer order/compare": generateIntegerOrderCompare,
  "integer add/subtract (mixed)": generateIntegerAddSubtractMixed,
  "quaternary to denary": generateQuaternaryToDenary,
};

describe("new practice generators produce well-formed problems", () => {
  for (const [name, gen] of Object.entries(NEW_GENERATORS)) {
    it(name, () => {
      for (const d of [1, 2, 3] as Difficulty[]) {
        for (let i = 0; i < 40; i++) assertWellFormed(gen(d));
      }
    });
  }
});

describe("quaternary answer matches its base-4 prompt", () => {
  it("converts base 4 correctly", () => {
    for (const d of [1, 2, 3] as Difficulty[]) {
      for (let i = 0; i < 40; i++) {
        const p = generateQuaternaryToDenary(d);
        const digits = p.prompt.match(/^(\d+)_\{4\}/)?.[1];
        expect(digits).toBeTruthy();
        if (p.answer.kind === "integer") {
          expect(p.answer.value).toBe(parseInt(digits!, 4));
        }
      }
    }
  });
});

describe("order/compare answer is one of the numbers asked about", () => {
  it("picks a value from the prompt", () => {
    for (let i = 0; i < 60; i++) {
      const p = generateIntegerOrderCompare(1);
      const nums = (p.prompt.match(/-?\d+/g) ?? []).map(Number);
      if (p.answer.kind === "integer") {
        expect(nums).toContain(p.answer.value);
      }
    }
  });
});
