import { Fraction } from "./fractions";
import { simplifyRatio } from "./ratios";

/** Parse a strict decimal string (digits and optional single dot). Rejects scientific notation, trailing letters, etc. */
export function parseStrictDecimal(input: string): number | null {
  const s = input.trim();
  if (!/^\d+(\.\d+)?$/.test(s)) return null;
  return parseFloat(s);
}

/** Parse a strict integer string (digits only, no dot or letters). */
export function parseStrictInt(input: string): number | null {
  const s = input.trim();
  if (!/^\d+$/.test(s)) return null;
  return parseInt(s, 10);
}

/** Parse a strict signed integer string. Reject decimals, scientific notation, and trailing text. */
export function parseStrictSignedInt(input: string): number | null {
  const s = input.trim();
  if (!/^-?\d+$/.test(s)) return null;
  return parseInt(s, 10);
}

/** Parse a strict signed decimal string. Reject scientific notation and trailing text. */
export function parseStrictSignedDecimal(input: string): number | null {
  const s = input.trim();
  if (!/^-?\d+(\.\d+)?$/.test(s)) return null;
  return parseFloat(s);
}

/**
 * The outcome of checking one submitted answer.
 * - `solved`        — correct and in the required form (fully done)
 * - `unsimplified`  — correct value, but still needs simplifying (a nudge, not done)
 * - `wrong`         — incorrect; `message` explains why
 *
 * A single `status` field means callers branch on one thing, instead of an
 * error-prone `correct`/`simplified` boolean pair.
 */
export type AnswerResult =
  | { status: "solved" }
  | { status: "unsimplified" }
  | { status: "wrong"; message: string };

export const SOLVED: AnswerResult = { status: "solved" };
export const UNSIMPLIFIED: AnswerResult = { status: "unsimplified" };
export function wrong(message: string): AnswerResult {
  return { status: "wrong", message };
}

export function checkFractionAnswer(
  userNum: number,
  userDen: number,
  expectedNum: number,
  expectedDen: number,
  requireSimplified: boolean = true,
): AnswerResult {
  if (userDen === 0) {
    return wrong("Denominator cannot be zero");
  }

  const user = new Fraction(userNum, userDen);
  const expected = new Fraction(expectedNum, expectedDen);

  if (!user.equals(expected)) {
    return wrong("That's not quite right. Try again!");
  }

  // Any non-zero denominator gives the same value for zero, so don't force 0/1 in the UI.
  if (user.isZero) {
    return SOLVED;
  }

  if (requireSimplified && !user.isSimplified) {
    return UNSIMPLIFIED;
  }

  return SOLVED;
}

export function checkDecimalAnswer(
  userValue: number,
  expectedValue: number,
  tolerance: number = 0.001,
): AnswerResult {
  if (Math.abs(userValue - expectedValue) <= tolerance) {
    return SOLVED;
  }
  return wrong("That's not quite right. Try again!");
}

export function checkRatioAnswer(
  userParts: number[],
  expectedParts: number[],
  requireSimplified: boolean = true,
): AnswerResult {
  if (userParts.length !== expectedParts.length) {
    return wrong("Check the number of parts in your ratio");
  }

  const userSimplified = simplifyRatio(userParts);
  const expectedSimplified = simplifyRatio(expectedParts);

  const isEquivalent = userSimplified.every((v, i) => v === expectedSimplified[i]);
  if (!isEquivalent) {
    return wrong("That's not quite right. Try again!");
  }

  if (requireSimplified) {
    const isAlreadySimplified = userParts.every((v, i) => v === userSimplified[i]);
    return isAlreadySimplified ? SOLVED : UNSIMPLIFIED;
  }

  return SOLVED;
}

export function checkIntegerAnswer(
  userValue: number,
  expectedValue: number,
): AnswerResult {
  return userValue === expectedValue ? SOLVED : wrong("That's not quite right. Try again!");
}

export function checkOrderingAnswer(
  userOrder: number[],
  expectedOrder: number[],
): AnswerResult {
  if (userOrder.length !== expectedOrder.length) {
    return wrong("Make sure you've ordered all the numbers");
  }
  const isCorrect = userOrder.every((v, i) => v === expectedOrder[i]);
  return isCorrect ? SOLVED : wrong("Check your ordering. Try again!");
}
