// Topic Tree — defines the dialogue branching structure for hybrid LLM mode.
//
// Shape: { nodes: { id: { title, context?, options?, triggers?, terminal?, intro? }, ... } }
//
// API (all callable via window.TopicTree):
//   getNode(id)            → { id, title, ... } | null
//   getRoot()              → root node
//   listNodes()            → array of all node ids
//   matchInput(id, text)   → matching child id from id's options, or null
//   replaceTree(json)      → swap entire tree (panel save)
//   exportTree()           → JSON-safe object (panel load)
//   resetToDefault()       → restore Malaysia Tourism default

const TopicTree = (() => {

  // Default tree — METASPEAK Malaysia Tourism. Edit via the Dialogue tab in
  // the panel without touching this file.
  const DEFAULT_TREE = {
    nodes: {
      root: {
        title: 'Pick a category to explore',
        intro: true,
        context: 'Welcome the user to METASPEAK Malaysia Tourism. Wira is the calm Perak-dialect host; Manja is the energetic Terengganu-dialect bear sidekick. Briefly invite them to choose from beach, city, nature, food, heritage.',
        options: ['beach', 'city', 'nature', 'food', 'heritage'],
      },

      // ── Top-level categories ────────────────────────────────────────
      beach: {
        title: 'Beach Holiday',
        triggers: [/\b(beach|pantai|laut|island|pulau|langkawi|redang|perhentian|tioman)\b/i],
        context: 'Malaysia islands and beaches — Langkawi, Perhentian, Redang, Tioman. Mention crystal water, snorkeling, island hopping. Get the user excited to drill into one.',
        options: ['beach_langkawi', 'beach_perhentian', 'beach_redang', 'beach_tioman'],
      },
      city: {
        title: 'City Escape',
        triggers: [/\b(city|bandar|kl|kuala lumpur|penang|georgetown|johor|melaka)\b/i],
        context: 'Malaysian cities — modern meets heritage. KL, Penang, Johor Bahru, Melaka. KLCC towers, Penang street food + art, JB shopping, Melaka history.',
        options: ['city_kl', 'city_penang', 'city_jb', 'city_melaka'],
      },
      nature: {
        title: 'Nature Adventure',
        triggers: [/\b(jungle|hutan|trekking|trek|waterfall|air terjun|taman negara|borneo|sabah|sarawak|cameron|kinabalu)\b/i],
        context: 'One of the world\'s oldest rainforests. Highlight Taman Negara, Sabah/Borneo wildlife, Cameron Highlands cool air + tea plantations.',
        options: ['nature_taman_negara', 'nature_borneo', 'nature_cameron', 'nature_kinabalu'],
      },
      food: {
        title: 'Food and Dining',
        triggers: [/\b(food|makan|makanan|nasi lemak|rendang|laksa|satay|cendol|hawker|kuih)\b/i],
        context: 'Malaysia is a food paradise — Malay, Chinese, Indian, peranakan all mix. Penang hawker food is UNESCO listed. Highlight nasi lemak, char kway teow, asam laksa, satay, cendol.',
        options: ['food_nasi_lemak', 'food_laksa', 'food_satay', 'food_cendol'],
      },
      heritage: {
        title: 'Cultural and Heritage',
        triggers: [/\b(culture|budaya|warisan|heritage|museum|muzium|temple|kuil|masjid|mosque)\b/i],
        context: 'Meeting point of Malay, Chinese, Indian, indigenous cultures. Melaka and Georgetown are UNESCO World Heritage Sites.',
        options: ['heritage_melaka', 'heritage_georgetown', 'heritage_kl_museum', 'heritage_sarawak'],
      },

      // ── Beach sub-topics (terminal) ─────────────────────────────────
      beach_langkawi: {
        title: 'Langkawi',
        terminal: true,
        triggers: [/langkawi/i],
        context: 'Langkawi: 99 islands, duty-free, Sky Bridge cable car, Pantai Cenang sunsets, mangrove tours, eagle feeding. UNESCO Geopark.',
      },
      beach_perhentian: {
        title: 'Pulau Perhentian',
        terminal: true,
        triggers: [/perhentian/i],
        context: 'Perhentian Islands (Besar + Kecil): backpacker turtle paradise, snorkeling with reef sharks, no-frills beach huts, ferry from Kuala Besut.',
      },
      beach_redang: {
        title: 'Pulau Redang',
        terminal: true,
        triggers: [/redang/i],
        context: 'Redang: marine park crystal waters, upscale resorts, Summer Bay & Laguna, famous from "Summer Holiday" film.',
      },
      beach_tioman: {
        title: 'Pulau Tioman',
        terminal: true,
        triggers: [/tioman/i],
        context: 'Tioman: duty-free island, Salang & Air Batang villages, jungle interior with monitor lizards, Asah Waterfall, ABC trail trek.',
      },

      // ── City sub-topics (terminal) ──────────────────────────────────
      city_kl: {
        title: 'Kuala Lumpur',
        terminal: true,
        triggers: [/\b(kuala lumpur|kl|klcc|petronas|bukit bintang|chinatown)\b/i],
        context: 'KL: Petronas Twin Towers Skybridge, KLCC Park, Bukit Bintang shopping + nightlife, Batu Caves north suburb, Chinatown Petaling Street, Kampung Baru Malay village.',
      },
      city_penang: {
        title: 'Penang',
        terminal: true,
        triggers: [/penang|georgetown/i],
        context: 'Penang: Georgetown UNESCO street art, hawker food capital (asam laksa, char kway teow), Penang Hill funicular, Kek Lok Si temple, Batu Ferringhi beach.',
      },
      city_jb: {
        title: 'Johor Bahru',
        terminal: true,
        triggers: [/\b(johor|jb|johor bahru)\b/i],
        context: 'JB: gateway to Singapore, Legoland Malaysia, Johor Premium Outlets, Sultan Abu Bakar Mosque, Desaru beach getaway nearby.',
      },
      city_melaka: {
        title: 'Melaka',
        terminal: true,
        triggers: [/melaka|malacca/i],
        context: 'Melaka: UNESCO heritage city, A Famosa fort, Jonker Walk night market, Stadthuys Dutch Square, Baba-Nyonya museum.',
      },

      // ── Nature sub-topics (terminal) ────────────────────────────────
      nature_taman_negara: {
        title: 'Taman Negara',
        terminal: true,
        triggers: [/taman negara/i],
        context: 'Taman Negara: 130 million-year-old rainforest, world\'s longest canopy walkway, river cruises, Lata Berkoh waterfall, hornbill spotting, Kuala Tahan base camp.',
      },
      nature_borneo: {
        title: 'Sabah & Borneo',
        terminal: true,
        triggers: [/sabah|borneo|sarawak|orangutan|sepilok|kinabatangan/i],
        context: 'Sabah/Borneo: Sepilok Orangutan Centre, Kinabatangan River cruise (proboscis monkeys), Sipadan diving, Mount Kinabalu climb, Mulu caves in Sarawak.',
      },
      nature_cameron: {
        title: 'Cameron Highlands',
        terminal: true,
        triggers: [/cameron|highlands/i],
        context: 'Cameron Highlands: BOH tea plantations, strawberry farms, Mossy Forest boardwalk, cool 18 degree Celsius air, scones at Smokehouse Hotel, Brinchang night market.',
      },
      nature_kinabalu: {
        title: 'Mount Kinabalu',
        terminal: true,
        triggers: [/kinabalu/i],
        context: 'Mount Kinabalu: 4095m highest peak in Southeast Asia, 2-day climb via Ranau or Kota Belud, summit at sunrise, Kinabalu Park UNESCO site, Poring Hot Springs at base.',
      },

      // ── Food sub-topics (terminal) ──────────────────────────────────
      food_nasi_lemak: {
        title: 'Nasi Lemak',
        terminal: true,
        triggers: [/nasi lemak/i],
        context: 'Nasi lemak: national dish — coconut rice, sambal, fried anchovies, peanuts, cucumber, boiled egg. Variants: ayam (chicken), kerang (cockles), sotong. Best from kampung-style stalls.',
      },
      food_laksa: {
        title: 'Laksa Penang',
        terminal: true,
        triggers: [/laksa/i],
        context: 'Asam laksa Penang: tamarind-mackerel sour soup, thick rice noodles, mint, pineapple, ginger flower, prawn paste. Distinct from creamy curry laksa.',
      },
      food_satay: {
        title: 'Satay',
        terminal: true,
        triggers: [/satay/i],
        context: 'Satay: marinated grilled skewers (chicken, beef, lamb), peanut sauce, ketupat rice cake, cucumber, onion. Kajang is satay capital. Pair with teh tarik.',
      },
      food_cendol: {
        title: 'Cendol & Kuih',
        terminal: true,
        triggers: [/cendol|kuih|dessert/i],
        context: 'Cendol: shaved ice with green pandan jelly, gula melaka palm sugar syrup, coconut milk. Kuih variety: kuih lapis, onde-onde, seri muka, kuih talam — colourful Malay teatime treats.',
      },

      // ── Heritage sub-topics (terminal) ──────────────────────────────
      heritage_melaka: {
        title: 'Melaka Bandaraya Bersejarah',
        terminal: true,
        triggers: [/melaka|malacca/i],
        context: 'Melaka heritage: Portuguese A Famosa, Dutch Stadthuys, Christ Church, St Paul Hill ruins, Baba-Nyonya peranakan houses, Cheng Hoon Teng oldest temple, trishaw rides.',
      },
      heritage_georgetown: {
        title: 'Georgetown Penang',
        terminal: true,
        triggers: [/georgetown/i],
        context: 'Georgetown: Ernest Zacharevic street art murals, clan jetties (Chew, Lim, Tan), Cheong Fatt Tze Blue Mansion, Kapitan Keling Mosque, Khoo Kongsi clan house.',
      },
      heritage_kl_museum: {
        title: 'Muzium Negara KL',
        terminal: true,
        triggers: [/muzium|museum negara/i],
        context: 'Muzium Negara KL: 4 galleries from pre-history through modern Malaysia. Traditional Minangkabau roof architecture. Nearby: Islamic Arts Museum, Masjid Negara.',
      },
      heritage_sarawak: {
        title: 'Kampung Budaya Sarawak',
        terminal: true,
        triggers: [/sarawak|kampung budaya|cultural village/i],
        context: 'Sarawak Cultural Village (Kuching): living museum of 7 ethnic groups — Iban longhouse, Bidayuh round house, Penan hut, Melanau tall house. Rainforest World Music Festival held here annually.',
      },
    },
  };

  // ── State ──────────────────────────────────────────────────────────
  let tree = null;

  // Cook string triggers ("/foo/i") into RegExp objects for matchInput
  function cookTriggers(treeObj) {
    if (!treeObj?.nodes) return treeObj;
    for (const n of Object.values(treeObj.nodes)) {
      if (Array.isArray(n.triggers)) {
        n.triggers = n.triggers.map(t => {
          if (t instanceof RegExp) return t;
          const m = String(t).match(/^\/(.+)\/([gimsuy]*)$/);
          return m ? new RegExp(m[1], m[2]) : new RegExp(String(t), 'i');
        });
      }
    }
    return treeObj;
  }

  function deepCloneDefault() {
    // Structured clone preserves RegExp objects in modern browsers; fall
    // back to JSON for safety.
    try {
      return cookTriggers(structuredClone(DEFAULT_TREE));
    } catch (e) {
      // JSON clone won't preserve RegExp, but we re-cook from string after
      const json = JSON.parse(JSON.stringify(DEFAULT_TREE, (k, v) =>
        v instanceof RegExp ? `/${v.source}/${v.flags}` : v));
      return cookTriggers(json);
    }
  }

  function resetToDefault() { tree = deepCloneDefault(); }
  resetToDefault();

  // ── Public API ─────────────────────────────────────────────────────

  function getNode(id) {
    if (!tree?.nodes?.[id]) return null;
    return { id, ...tree.nodes[id] };
  }
  function getRoot() { return getNode('root'); }
  function listNodes() { return Object.keys(tree.nodes); }

  /** Match user input against a node's children. Returns matching child id,
   *  or null. Two-pass: exact title match first (button click), then trigger
   *  regex match (free-form input). */
  function matchInput(nodeId, userInput) {
    const node = tree.nodes[nodeId];
    if (!node?.options || !userInput) return null;
    const text = String(userInput).trim();
    if (!text) return null;
    const lower = text.toLowerCase();
    // Pass 1: exact title match
    for (const childId of node.options) {
      const child = tree.nodes[childId];
      if (child?.title && child.title.toLowerCase() === lower) return childId;
    }
    // Pass 2: trigger regex
    for (const childId of node.options) {
      const child = tree.nodes[childId];
      if (!child?.triggers) continue;
      for (const t of child.triggers) {
        try {
          const re = (t instanceof RegExp) ? t : new RegExp(String(t), 'i');
          if (re.test(text)) return childId;
        } catch (e) { /* skip bad regex */ }
      }
    }
    return null;
  }

  /** Replace tree from a JSON-serialized object (panel Save). */
  function replaceTree(rawTree) {
    if (!rawTree || typeof rawTree !== 'object') {
      throw new Error('Tree must be an object');
    }
    if (!rawTree.nodes || typeof rawTree.nodes !== 'object') {
      throw new Error('Tree must have a "nodes" object');
    }
    if (!rawTree.nodes.root) {
      throw new Error('Tree must have a "root" node under nodes');
    }
    tree = cookTriggers(JSON.parse(JSON.stringify(rawTree)));
  }

  /** Serialize current tree to a JSON-safe object (RegExp → strings). */
  function exportTree() {
    if (!tree?.nodes) return { nodes: {} };
    const out = { nodes: {} };
    for (const [id, n] of Object.entries(tree.nodes)) {
      const exp = {};
      for (const [k, v] of Object.entries(n)) {
        if (k === 'triggers' && Array.isArray(v)) {
          exp.triggers = v.map(t => {
            if (t instanceof RegExp) return `/${t.source}/${t.flags}`;
            return String(t);
          });
        } else {
          exp[k] = v;
        }
      }
      out.nodes[id] = exp;
    }
    return out;
  }

  return {
    getNode, getRoot, listNodes, matchInput,
    replaceTree, exportTree, resetToDefault,
    get _raw() { return tree; },
  };
})();

window.TopicTree = TopicTree;
