import type { MathProblem, Difficulty } from "../types";
import { randomInt, randomChoice } from "@/lib/utils";
import { Fraction, lcm } from "@/lib/math/fractions";

let counter = 0;
function nextId() {
  return `fp-${++counter}-${Date.now()}`;
}

export function generateFractionAddSubtract(difficulty: Difficulty): MathProblem {
  const operation = randomChoice(["add", "subtract"] as const);
  const op = operation === "add" ? "+" : "-";

  let n1: number, d1: number, n2: number, d2: number;

  if (difficulty === 1) {
    // Common denominators
    d1 = randomChoice([2, 3, 4, 5, 6, 8, 10, 12]);
    d2 = d1;
    n1 = randomInt(1, d1 - 1);
    n2 = operation === "subtract" ? randomInt(1, n1) : randomInt(1, d1 - n1);
  } else if (difficulty === 2) {
    // One denominator is a multiple of the other
    d1 = randomChoice([2, 3, 4, 5, 6]);
    const multiplier = randomChoice([2, 3]);
    d2 = d1 * multiplier;
    n1 = randomInt(1, d1 - 1);
    n2 = randomInt(1, d2 - 1);
  } else {
    // Unlike denominators
    d1 = randomChoice([3, 4, 5, 7, 8, 9]);
    d2 = randomChoice([3, 4, 5, 7, 8, 9].filter((d) => d !== d1));
    n1 = randomInt(1, d1 - 1);
    n2 = randomInt(1, d2 - 1);
  }

  const prompt = `\\frac{${n1}}{${d1}} ${op} \\frac{${n2}}{${d2}}`;
  const commonDen = lcm(d1, d2);
  const convertedN1 = n1 * (commonDen / d1);
  const convertedN2 = n2 * (commonDen / d2);
  const rawNum = operation === "add"
    ? convertedN1 + convertedN2
    : convertedN1 - convertedN2;
  const { n: ansNum, d: ansDen } = new Fraction(rawNum, commonDen).simplified();

  const steps = d1 === d2
    ? [
        {
          explanation: `The denominators already match, so keep ${d1} as the denominator.`,
          math: `\\frac{${n1}}{${d1}} ${op} \\frac{${n2}}{${d2}}`,
        },
        {
          explanation: `${operation === "add" ? "Add" : "Subtract"} the numerators and keep the denominator the same.`,
          math: `\\frac{${n1}}{${d1}} ${op} \\frac{${n2}}{${d2}} = \\frac{${rawNum}}{${d1}}`,
        },
        ...(ansNum !== rawNum || ansDen !== d1
          ? [{
              explanation: "Simplify the fraction.",
              math: `\\frac{${rawNum}}{${d1}} = \\frac{${ansNum}}{${ansDen}}`,
            }]
          : []),
      ]
    : [
        {
          explanation: `Find the lowest common denominator: LCM(${d1}, ${d2}) = ${commonDen}.`,
          math: `\\text{LCD} = ${commonDen}`,
        },
        {
          explanation: "Rewrite both fractions with that denominator.",
          math: `\\frac{${n1}}{${d1}} = \\frac{${convertedN1}}{${commonDen}} \\quad \\frac{${n2}}{${d2}} = \\frac{${convertedN2}}{${commonDen}}`,
        },
        {
          explanation: `${operation === "add" ? "Add" : "Subtract"} the numerators and keep the common denominator.`,
          math: `\\frac{${convertedN1}}{${commonDen}} ${op} \\frac{${convertedN2}}{${commonDen}} = \\frac{${rawNum}}{${commonDen}}`,
        },
        ...(ansNum !== rawNum || ansDen !== commonDen
          ? [{
              explanation: "Simplify the fraction.",
              math: `\\frac{${rawNum}}{${commonDen}} = \\frac{${ansNum}}{${ansDen}}`,
            }]
          : []),
      ];

  return {
    id: nextId(),
    prompt,
    answer: { kind: "fraction", numerator: ansNum, denominator: ansDen },
    hints: [
      d1 === d2 ? "The denominators are already the same!" : "Find a common denominator first",
      d1 !== d2 ? "Rewrite both fractions using that denominator" : `Just ${operation} the numerators`,
    ],
    steps,
  };
}

export function generateFractionMultiply(difficulty: Difficulty): MathProblem {
  const denChoices = difficulty === 1 ? [2, 3, 4, 5] : difficulty === 2 ? [2, 3, 4, 5, 6, 7, 8] : [3, 5, 7, 9, 11, 12, 15];
  const d1 = randomChoice(denChoices);
  const d2 = randomChoice(denChoices);
  const n1 = randomInt(1, d1 - 1);
  const n2 = randomInt(1, d2 - 1);

  const { n: ansNum, d: ansDen } = Fraction.of(n1, d1).times(Fraction.of(n2, d2)).simplified();

  return {
    id: nextId(),
    prompt: `\\frac{${n1}}{${d1}} \\times \\frac{${n2}}{${d2}}`,
    answer: { kind: "fraction", numerator: ansNum, denominator: ansDen },
    hints: [
      "Multiply numerator × numerator",
      "Multiply denominator × denominator",
      "Simplify if possible",
    ],
    steps: [
      { explanation: `Multiply numerators: ${n1} × ${n2} = ${n1 * n2}` },
      { explanation: `Multiply denominators: ${d1} × ${d2} = ${d1 * d2}` },
      { explanation: "Simplify", math: `\\frac{${ansNum}}{${ansDen}}` },
    ],
  };
}

export function generateFractionDivide(difficulty: Difficulty): MathProblem {
  const denChoices = difficulty === 1 ? [2, 3, 4, 5] : difficulty === 2 ? [2, 3, 4, 5, 6, 7, 8] : [3, 5, 7, 9, 11, 14, 15];
  const d1 = randomChoice(denChoices);
  const d2 = randomChoice(denChoices);
  const n1 = randomInt(1, d1 - 1);
  const n2 = randomInt(1, d2 - 1);

  const { n: ansNum, d: ansDen } = Fraction.of(n1, d1).dividedBy(Fraction.of(n2, d2)).simplified();

  return {
    id: nextId(),
    prompt: `\\frac{${n1}}{${d1}} \\div \\frac{${n2}}{${d2}}`,
    answer: { kind: "fraction", numerator: ansNum, denominator: ansDen },
    hints: [
      "Keep the first fraction the same",
      "Flip the second fraction (reciprocal)",
      "Then multiply",
    ],
    steps: [
      { explanation: `Keep ${n1}/${d1}, flip ${n2}/${d2} to ${d2}/${n2}` },
      { explanation: `Multiply: ${n1}×${d2} / ${d1}×${n2}` },
      { explanation: "Simplify", math: `\\frac{${ansNum}}{${ansDen}}` },
    ],
  };
}

export function generateFractionOfQuantity(difficulty: Difficulty): MathProblem {
  const denChoices = difficulty === 1 ? [2, 4, 5, 10] : difficulty === 2 ? [3, 4, 5, 6, 8] : [7, 8, 9, 12, 15];
  const den = randomChoice(denChoices);
  const num = randomInt(1, den - 1);
  const multiplier = randomChoice(difficulty === 1 ? [2, 4, 5, 10] : [3, 6, 7, 8, 9, 12]);
  const quantity = den * multiplier;
  const answer = num * multiplier;

  return {
    id: nextId(),
    prompt: `\\frac{${num}}{${den}} \\text{ of } ${quantity}`,
    answer: { kind: "integer", value: answer },
    hints: [
      `Divide ${quantity} by ${den} first`,
      `Then multiply by ${num}`,
    ],
    steps: [
      { explanation: `Divide the quantity by the denominator: ${quantity} ÷ ${den} = ${quantity / den}` },
      { explanation: `Multiply by the numerator: ${quantity / den} × ${num} = ${answer}` },
    ],
  };
}

/** A proper-fraction numerator with a random sign, magnitude 1..den-1. */
function signedProperNum(den: number): number {
  const mag = randomInt(1, Math.max(1, den - 1));
  return Math.random() < 0.5 ? -mag : mag;
}

/** Two proper fractions where at least one is negative (this is the negative-fractions topic). */
function signedFractionPair(d1: number, d2: number): [Fraction, Fraction] {
  let n1 = signedProperNum(d1);
  let n2 = signedProperNum(d2);
  if (n1 > 0 && n2 > 0) {
    if (Math.random() < 0.5) n1 = -n1;
    else n2 = -n2;
  }
  return [new Fraction(n1, d1), new Fraction(n2, d2)];
}

function signedOperand(f: Fraction): string {
  return f.n < 0 ? `\\left(${f.toSignedKatex()}\\right)` : f.toSignedKatex();
}

export function generateSignedFractionAddSubtract(difficulty: Difficulty): MathProblem {
  const operation = randomChoice(["+", "-"] as const);
  const dens = difficulty === 1 ? [2, 3, 4] : difficulty === 2 ? [2, 3, 4, 5, 6] : [3, 4, 5, 6, 8];
  const d1 = randomChoice(dens);
  const d2 = randomChoice(dens);
  const [a, b] = signedFractionPair(d1, d2);
  const result = (operation === "+" ? a.plus(b) : a.minus(b)).simplified();

  return {
    id: nextId(),
    prompt: `${a.toSignedKatex()} ${operation} ${signedOperand(b)}`,
    answer: { kind: "fraction", numerator: result.n, denominator: result.d },
    hints: [
      d1 === d2 ? "The denominators already match." : "Find a common denominator first.",
      "Combine the numerators using the integer sign rules (subtracting a negative adds).",
      "Simplify your answer.",
    ],
    steps: [
      {
        explanation: d1 === d2 ? `Keep the denominator ${d1}.` : `Use a common denominator: LCM(${d1}, ${d2}) = ${lcm(d1, d2)}.`,
      },
      { explanation: `${operation === "+" ? "Add" : "Subtract"} the numerators, watching the signs.` },
      { explanation: "Simplify to lowest terms.", math: result.toSignedKatex() },
    ],
  };
}

export function generateSignedFractionMultiplyDivide(difficulty: Difficulty): MathProblem {
  const operation = randomChoice(["\\times", "\\div"] as const);
  const dens = difficulty === 1 ? [2, 3, 4] : difficulty === 2 ? [2, 3, 4, 5] : [3, 4, 5, 6, 7];
  const d1 = randomChoice(dens);
  const d2 = randomChoice(dens);
  const [a, b] = signedFractionPair(d1, d2);
  const result = (operation === "\\times" ? a.times(b) : a.dividedBy(b)).simplified();
  const sameSign = a.n < 0 === b.n < 0;

  return {
    id: nextId(),
    prompt: `${a.toSignedKatex()} ${operation} ${signedOperand(b)}`,
    answer: { kind: "fraction", numerator: result.n, denominator: result.d },
    hints: [
      operation === "\\div" ? "Keep, change, flip: multiply by the reciprocal." : "Multiply numerators and denominators.",
      `The signs are ${sameSign ? "the same, so the answer is positive" : "different, so the answer is negative"}.`,
      "Simplify your answer.",
    ],
    steps: [
      operation === "\\div"
        ? { explanation: "Multiply by the reciprocal of the second fraction (keep, change, flip)." }
        : { explanation: "Multiply the numerators and multiply the denominators." },
      { explanation: `Same signs give positive, different signs give negative — here the answer is ${result.n < 0 ? "negative" : "positive"}.` },
      { explanation: "Simplify to lowest terms.", math: result.toSignedKatex() },
    ],
  };
}

export function generateFractionBodmas(difficulty: Difficulty): MathProblem {
  const dens = difficulty === 1 ? [2, 3, 4] : difficulty === 2 ? [2, 3, 4, 5] : [2, 3, 4, 5, 6];
  const mk = () => {
    const d = randomChoice(dens);
    return new Fraction(randomInt(1, d - 1), d);
  };
  const f1 = mk(), f2 = mk(), f3 = mk();
  const multFirst = Math.random() < 0.5;

  let prompt: string;
  let result: Fraction;
  if (multFirst) {
    const prod = f2.times(f3);
    // avoid a negative answer in this (positive-fractions) unit
    const op = f1.toDecimal() >= prod.toDecimal() && Math.random() < 0.5 ? "-" : "+";
    result = (op === "+" ? f1.plus(prod) : f1.minus(prod)).simplified();
    prompt = `${f1.toKatex()} ${op} ${f2.toKatex()} \\times ${f3.toKatex()}`;
  } else {
    const prod = f1.times(f2);
    const op = prod.toDecimal() >= f3.toDecimal() && Math.random() < 0.5 ? "-" : "+";
    result = (op === "+" ? prod.plus(f3) : prod.minus(f3)).simplified();
    prompt = `${f1.toKatex()} \\times ${f2.toKatex()} ${op} ${f3.toKatex()}`;
  }

  return {
    id: nextId(),
    prompt,
    answer: { kind: "fraction", numerator: result.n, denominator: result.d },
    hints: [
      "BODMAS: do the multiplication before the addition or subtraction.",
      "Multiply the two fractions first, then combine with a common denominator.",
    ],
    steps: [
      { explanation: "Multiply first (that's the B-O-D-M before A-S)." },
      { explanation: "Then add or subtract, using a common denominator." },
      { explanation: "Simplify.", math: result.toKatex() },
    ],
  };
}

/** Combined signed-fraction practice covering +, -, x and / (Unit 5 topic). */
export function generateSignedFraction(difficulty: Difficulty): MathProblem {
  const operation = randomChoice(["+", "-", "\\times", "\\div"] as const);
  const dens = difficulty === 1 ? [2, 3, 4] : difficulty === 2 ? [2, 3, 4, 5] : [3, 4, 5, 6];
  const [a, b] = signedFractionPair(randomChoice(dens), randomChoice(dens));

  let result: Fraction;
  if (operation === "+") result = a.plus(b);
  else if (operation === "-") result = a.minus(b);
  else if (operation === "\\times") result = a.times(b);
  else result = a.dividedBy(b);
  result = result.simplified();

  return {
    id: nextId(),
    prompt: `${a.toSignedKatex()} ${operation} ${signedOperand(b)}`,
    answer: { kind: "fraction", numerator: result.n, denominator: result.d },
    hints: [
      "Apply the integer sign rules as you work.",
      "Give your answer in its simplest form.",
    ],
    steps: [
      { explanation: "Work through the operation, watching the signs." },
      { explanation: "Simplify.", math: result.toSignedKatex() },
    ],
  };
}

export function generateWholeFromFraction(difficulty: Difficulty): MathProblem {
  const denChoices = difficulty === 1 ? [2, 3, 4, 5] : difficulty === 2 ? [3, 5, 6, 8] : [7, 8, 9, 12];
  const den = randomChoice(denChoices);
  const num = randomInt(1, den - 1);
  const whole = den * randomChoice(difficulty === 1 ? [2, 3, 4, 5] : [3, 5, 6, 7, 8, 10]);
  const part = (num / den) * whole;

  return {
    id: nextId(),
    prompt: `\\frac{${num}}{${den}} \\text{ of a number is } ${part}\\text{. Find the number.}`,
    answer: { kind: "integer", value: whole },
    hints: [
      `If ${num}/${den} of the number is ${part}...`,
      `Find 1/${den} first by dividing ${part} by ${num}`,
      `Then multiply by ${den} to get the whole`,
    ],
    steps: [
      { explanation: `1/${den} of the number = ${part} ÷ ${num} = ${part / num}` },
      { explanation: `The whole number = ${part / num} × ${den} = ${whole}` },
    ],
  };
}
