/**
 * Markdown-parser för recept
 * Extraherar namn, beskrivning, instruktioner och ingredienser från Markdown.
 */

// ============================================================================
// Local Type Definitions
// ============================================================================

export interface ParsedIngredient {
  rawName: string;
  quantity: number;
  unit: string;
  note: string | null;
  alternatives: string[]; // uppdelning av "ris eller couscous" → ["ris", "couscous"]
}

export interface ParsedRecipe {
  name: string;
  description: string;
  instructions: string;
  ingredients: ParsedIngredient[];
}

// ============================================================================
// Module-level constants (hoisted for performance)
// ============================================================================

const KNOWN_UNITS = new Set([
  'g', 'kg', 'hg', 'mg', 'ml', 'dl', 'l', 'tl',
  'st', 'tsk', 'msk', 'krm', 'matsled', 'tesled',
  'pris', 'portion', 'port', 'burk', 'förp', 'paket', 'efter smak', 'klyfta',
]);

const H1_RE = /^#\s+/;
const H2_RE = /^##\s+/;
const INGREDIENT_HEADING_RE = /ingrediens/;
const INSTRUCTION_HEADING_RE = /instruktion|tillagning|gör så här|steg|tillväg|metod/;
const BULLET_RE = /^[-*]\s+/;
const PAREN_NOTE_RE = /\(([^)]+)\)\s*$/;
const FRACTION_RE = /^(\d+)?\s*(\d+)\s*\/\s*([\d.]+)\s+(\S+)\s+(.+)$/;
const RANGE_RE = /^(?:ca\.?\s+)?(\d+(?:[.,]\d+)?)\s*[-–]\s*(?:ca\.?\s+)?(\d+(?:[.,]\d+)?)\s+(\S+)\s+(.+)$/i;
const QTY_UNIT_NAME_RE = /^(\d+(?:[.,]\d+)?)\s+(\S+)\s+(.+)$/;
const QTY_NAME_RE = /^(\d+(?:[.,]\d+)?)\s+(.+)$/;
const ALTERNATIVES_RE = /\s+eller\s+/i;

// ============================================================================
// Parser Functions
// ============================================================================

/**
 * Parsar ett recept i Markdown-format och extraherar namn, beskrivning,
 * instruktioner och ingredienser.
 *
 * Förväntat format:
 *   # Receptnamn
 *   Beskrivning (valfritt stycke efter titeln)
 *
 *   ## Ingredienser
 *   - 400 g kycklingfilé
 *   - 2 dl grädde (eller crème fraiche)
 *
 *   ## Instruktioner
 *   1. Stek kycklingen …
 */
export function parseRecipeMarkdown(markdown: string): ParsedRecipe {
  const lines = markdown.split('\n');

  let name = '';
  let description = '';
  let instructions = '';
  const ingredients: ParsedIngredient[] = [];

  let currentSection: 'none' | 'description' | 'ingredients' | 'instructions' = 'none';
  const descriptionLines: string[] = [];
  const instructionLines: string[] = [];

  for (const line of lines) {
    const trimmed = line.trim();

    // H1 — receptnamn
    if (H1_RE.test(trimmed) && !trimmed.startsWith('##')) {
      name = trimmed.replace(H1_RE, '').trim();
      currentSection = 'description';
      continue;
    }

    // H2 — sektionsrubriker
    if (H2_RE.test(trimmed)) {
      const heading = trimmed.replace(H2_RE, '').trim().toLowerCase();
      if (INGREDIENT_HEADING_RE.test(heading)) {
        currentSection = 'ingredients';
      } else if (INSTRUCTION_HEADING_RE.test(heading)) {
        currentSection = 'instructions';
      } else {
        currentSection = 'none';
      }
      continue;
    }

    // Samla rader beroende på sektion
    switch (currentSection) {
      case 'description':
        if (trimmed.length > 0) {
          descriptionLines.push(trimmed);
        }
        break;

      case 'ingredients':
        if (BULLET_RE.test(trimmed)) {
          const ingredientText = trimmed.replace(BULLET_RE, '');
          ingredients.push(parseIngredientLine(ingredientText));
        }
        break;

      case 'instructions':
        if (trimmed.length > 0) {
          instructionLines.push(trimmed);
        }
        break;
    }
  }

  description = descriptionLines.join('\n');
  instructions = instructionLines.join('\n');

  return { name, description, instructions, ingredients };
}

/**
 * Parsar en ingrediensrad, t.ex.:
 *   "400 g kycklingfilé"
 *   "2 dl grädde (eller crème fraiche)"
 *   "1 1/2 dl crème fraiche"
 *   "1 polka- eller gulbeta"
 *   "1 kruka basilika"
 *   "salt"
 */
function parseIngredientLine(text: string): ParsedIngredient {
  const trimmed = text.trim();

  // Hjälpfunktion: splittar rawName på " eller " och returnerar alternatives
  const toAlternatives = (rawName: string): string[] =>
    ALTERNATIVES_RE.test(rawName)
      ? rawName.split(ALTERNATIVES_RE).map((s) => s.trim()).filter(Boolean)
      : [rawName];

  // Extrahera eventuell parentes-not i slutet
  let note: string | null = null;
  let main = trimmed;
  const parenMatch = trimmed.match(PAREN_NOTE_RE);
  if (parenMatch) {
    note = parenMatch[1].trim();
    main = trimmed.slice(0, parenMatch.index).trim();
  }

  // Försök matcha bråk först: "1 1/2 dl crème fraiche" eller "1/2 dl mjölk"
  const fractionMatch = main.match(FRACTION_RE);
  if (fractionMatch) {
    const whole = fractionMatch[1] ? parseFloat(fractionMatch[1]) : 0;
    const quantity = whole + parseFloat(fractionMatch[2]) / parseFloat(fractionMatch[3]);
    const candidateUnit = fractionMatch[4].toLowerCase();
    if (KNOWN_UNITS.has(candidateUnit)) {
      const rawName = fractionMatch[5].trim();
      return { quantity, unit: candidateUnit, rawName, note, alternatives: toAlternatives(rawName) };
    }
  }

  // Försök matcha intervall: "600 - ca 700 g kycklingfilé" eller "ca 600–700 g"
  // Använder medelvärdet av intervallet som kvantitet.
  const rangeMatch = main.match(RANGE_RE);
  if (rangeMatch) {
    const candidateUnit = rangeMatch[3].toLowerCase();
    if (KNOWN_UNITS.has(candidateUnit)) {
      const lo = parseNumber(rangeMatch[1]);
      const hi = parseNumber(rangeMatch[2]);
      const rawName = rangeMatch[4].trim();
      return { quantity: (lo + hi) / 2, unit: candidateUnit, rawName, note, alternatives: toAlternatives(rawName) };
    }
  }

  // Försök matcha "kvantitet enhet namn" — t.ex. "400 g kycklingfilé" eller "2.5 dl grädde"
  const fullMatch = main.match(QTY_UNIT_NAME_RE);
  if (fullMatch) {
    const candidateUnit = fullMatch[2].toLowerCase();
    if (KNOWN_UNITS.has(candidateUnit)) {
      const rawName = fullMatch[3].trim();
      return { quantity: parseNumber(fullMatch[1]), unit: candidateUnit, rawName, note, alternatives: toAlternatives(rawName) };
    }
    // Inte känd enhet — behandla "kvantitet ord1 ord2..." utan enhet
    const rawName = fullMatch[2] + ' ' + fullMatch[3];
    return { quantity: parseNumber(fullMatch[1]), unit: 'st', rawName, note, alternatives: toAlternatives(rawName) };
  }

  // Försök matcha "kvantitet namn" utan enhet — t.ex. "3 ägg"
  const noUnitMatch = main.match(QTY_NAME_RE);
  if (noUnitMatch) {
    const rawName = noUnitMatch[2].trim();
    return { quantity: parseNumber(noUnitMatch[1]), unit: 'st', rawName, note, alternatives: toAlternatives(rawName) };
  }

  // Bara ett namn, ingen kvantitet — t.ex. "salt"
  return { quantity: 0, unit: '', rawName: main, note, alternatives: toAlternatives(main) };
}

function parseNumber(s: string): number {
  return parseFloat(s.replace(',', '.'));
}
