#!/usr/bin/env node
/**
 * Lightweight TOML parser — handles the subset needed by OpenClaw config.
 * No external dependencies.
 *
 * Supports:
 *   key = "string" | 'string literal'
 *   key = 123 | -1.5 | true | false
 *   [table]
 *   [table.subtable]
 *   [[array_of_tables]]
 *   key = [1, 2, "a"]
 *   key = { nested = "inline" }
 *   # comments
 *   Multi-line basic strings (""")
 */

function parseTOML(content) {
  const lines = content.split('\n');
  const root = {};
  let currentTable = root;
  let currentTablePath = [];
  let currentArrayPath = null;
  let currentArrayIndex = 0;

  let i = 0;
  while (i < lines.length) {
    let line = lines[i];

    // Skip empty lines and comments
    if (line.trim() === '' || line.trim().startsWith('#')) {
      i++;
      continue;
    }

    // Multi-line strings (""")
    if (line.includes('"""')) {
      const keyPart = line.split('"""')[0];
      const keyMatch = keyPart.match(/^(\S+)\s*=\s*$/);
      if (keyMatch) {
        const key = keyMatch[1];
        let value = '';
        i++;
        while (i < lines.length && !lines[i].includes('"""')) {
          value += (value ? '\n' : '') + lines[i];
          i++;
        }
        setNestedValue(currentTable, key, value);
        i++;
        continue;
      }
    }

    // Table header: [table] or [table.sub.table]
    const tableMatch = line.match(/^\s*\[([^\]]+)\]\s*$/);
    if (tableMatch) {
      const path = tableMatch[1].trim();
      currentTablePath = path.split('.').map(s => s.trim());
      currentTable = ensurePath(root, currentTablePath);
      currentArrayPath = null;
      i++;
      continue;
    }

    // Array of tables: [[array_of_tables]]
    const arrayTableMatch = line.match(/^\s*\[\[([^\]]+)\]\]\s*$/);
    if (arrayTableMatch) {
      const path = arrayTableMatch[1].trim();
      currentTablePath = path.split('.').map(s => s.trim());
      currentArrayPath = currentTablePath.slice();

      // Navigate to parent table
      const parentPath = currentTablePath.slice(0, -1);
      const lastKey = currentTablePath[currentTablePath.length - 1];
      const parent = ensurePath(root, parentPath);

      if (!parent[lastKey]) parent[lastKey] = [];
      const newEntry = {};
      parent[lastKey].push(newEntry);
      currentTable = newEntry;
      currentArrayIndex = parent[lastKey].length - 1;
      i++;
      continue;
    }

    // Key-value pair
    const kvMatch = line.match(/^\s*(\S+)\s*=\s*(.+)$/);
    if (kvMatch) {
      const key = kvMatch[1].trim();
      const rawValue = kvMatch[2].trim();
      const value = parseValue(rawValue);
      setNestedValue(currentTable, key, value);
      i++;
      continue;
    }

    // Unrecognized line
    i++;
  }

  return root;
}

/**
 * Parse a TOML value.
 */
function parseValue(raw) {
  raw = raw.trim();

  // Remove trailing comment
  const commentIdx = findCommentStart(raw);
  raw = commentIdx >= 0 ? raw.substring(0, commentIdx).trim() : raw;

  // Array: [1, 2, "a"]
  if (raw.startsWith('[') && raw.endsWith(']')) {
    const inner = raw.slice(1, -1).trim();
    if (inner === '') return [];
    return splitArrayItems(inner).map(parseValue);
  }

  // Inline table: { key = "val" }
  if (raw.startsWith('{') && raw.endsWith('}')) {
    return parseInlineTable(raw);
  }

  // Boolean
  if (raw === 'true') return true;
  if (raw === 'false') return false;

  // String: "..." or '...'
  if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))) {
    return raw.slice(1, -1).replace(/\\"/g, '"').replace(/\\n/g, '\n').replace(/\\t/g, '\t');
  }

  // Number
  if (/^-?\d+(\.\d+)?(e[+-]?\d+)?$/i.test(raw) || /^-?inf$/i.test(raw) || /^nan$/i.test(raw)) {
    return Number(raw);
  }

  // Fallback: raw string
  return raw;
}

function findCommentStart(str) {
  let inString = false;
  let stringChar = '';
  for (let i = 0; i < str.length; i++) {
    if (!inString && (str[i] === '"' || str[i] === "'")) {
      inString = true;
      stringChar = str[i];
    } else if (inString && str[i] === stringChar && str[i-1] !== '\\') {
      inString = false;
    } else if (!inString && str[i] === '#') {
      return i;
    }
  }
  return -1;
}

function splitArrayItems(str) {
  const items = [];
  let depth = 0;
  let inString = false;
  let stringChar = '';
  let current = '';

  for (let i = 0; i < str.length; i++) {
    const ch = str[i];

    if (!inString && (ch === '"' || ch === "'")) {
      inString = true;
      stringChar = ch;
      current += ch;
    } else if (inString && ch === stringChar && str[i-1] !== '\\') {
      inString = false;
      current += ch;
    } else if (!inString && (ch === '[' || ch === '{')) {
      depth++;
      current += ch;
    } else if (!inString && (ch === ']' || ch === '}')) {
      depth--;
      current += ch;
    } else if (!inString && depth === 0 && ch === ',') {
      items.push(current);
      current = '';
    } else {
      current += ch;
    }
  }

  if (current.trim()) items.push(current);
  return items;
}

function parseInlineTable(raw) {
  const obj = {};
  const inner = raw.slice(1, -1).trim();
  if (!inner) return obj;

  const pairs = splitArrayItems(inner);
  for (const pair of pairs) {
    const eqIdx = pair.indexOf('=');
    if (eqIdx < 0) continue;
    const key = pair.substring(0, eqIdx).trim();
    const value = pair.substring(eqIdx + 1).trim();
    obj[key] = parseValue(value);
  }
  return obj;
}

/**
 * Ensure a nested path exists in object, creating tables as needed.
 */
function ensurePath(obj, pathArr) {
  let current = obj;
  for (const part of pathArr) {
    if (!current[part]) current[part] = {};
    current = current[part];
  }
  return current;
}

/**
 * Set a value at a potentially nested key path.
 */
function setNestedValue(obj, key, value) {
  obj[key] = value;
}

// ─── Stringify (TOML → String) ───────────────────────────────────

function stringifyTOML(obj, prefix = '') {
  const lines = [];

  for (const [key, value] of Object.entries(obj)) {
    const fullKey = prefix ? `${prefix}.${key}` : key;

    if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
      // Check if we should treat as table
      if (Object.keys(value).length > 0) {
        lines.push(`\n[${fullKey}]`);
        for (const [subKey, subValue] of Object.entries(value)) {
          lines.push(formatTOMLValue(subKey, subValue));
        }
      }
    } else if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' && !Array.isArray(value[0])) {
      // Array of tables
      for (const item of value) {
        lines.push(`\n[[${fullKey}]]`);
        for (const [subKey, subValue] of Object.entries(item)) {
          lines.push(formatTOMLValue(subKey, subValue));
        }
      }
    } else {
      lines.push(formatTOMLValue(key, value));
    }
  }

  return lines.join('\n');
}

function formatTOMLValue(key, value) {
  if (typeof value === 'string') {
    if (value.includes('\n')) {
      return `${key} = """\n${value}\n"""`;
    }
    return `${key} = "${value.replace(/"/g, '\\"')}"`;
  }
  if (typeof value === 'boolean') return `${key} = ${value}`;
  if (typeof value === 'number') return `${key} = ${Number.isInteger(value) ? value : value}`;
  if (value === null || value === undefined) return `# ${key} = null`;
  if (Array.isArray(value)) return `${key} = ${JSON.stringify(value)}`;
  return `${key} = ${JSON.stringify(value)}`;
}

// ─── Module exports ──────────────────────────────────────────────

module.exports = { parseTOML, stringifyTOML };
