#!/usr/bin/env node
/**
 * Usage:
 *   npx @adia-ai/web-components doc button
 *   npx @adia-ai/web-components doc field
 *   npx @adia-ai/web-components doc            # list all components
 *
 * Prints a quick summary + live demo URL + first example fragment for any
 * AdiaUI component. Reads from the installed package's yaml/.examples.md
 * (falls back to <name>.examples.html if .md is not present).
 *
 * No external yaml dependency — uses a minimal regex-based parser scoped
 * to the small subset of fields we need: name, tag, component,
 * description, props, slots, events.
 */

import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
import { resolve, dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// bin/ lives at packages/web-components/bin/, components/ is one level up
const COMPONENTS_DIR = resolve(__dirname, '..', 'components');
const DEMO_BASE = 'https://ui-kit.exe.xyz/site/components';

/* ------------------------------------------------------------------ */
/*  Minimal YAML extractor                                             */
/* ------------------------------------------------------------------ */
/**
 * Parses just the subset of YAML used by AdiaUI component yaml files:
 *  - top-level scalar keys (name, tag, component, description, …)
 *  - top-level mapping keys whose values are mappings of leaf scalars
 *    (props, slots, events)
 *
 * Returns:
 *   {
 *     name, tag, component, description,
 *     props:  { propName:  { description, type, default, enum?, ... } },
 *     slots:  { slotName:  { description } },
 *     events: { eventName: { description, type? } },
 *   }
 *
 * Defensive: any parse error returns a partial object plus an `_errors`
 * array — the caller decides how to surface failures.
 */
function parseYaml(source) {
  const out = {
    name: '',
    tag: '',
    component: '',
    description: '',
    props: {},
    slots: {},
    events: {},
    _errors: [],
  };

  // Strip comments + trailing whitespace, keep blank lines for block detection
  const rawLines = source.split(/\r?\n/);
  const lines = rawLines.map((l) => {
    // strip line comments unless inside a quoted string (rough heuristic)
    const m = l.match(/^([^#"']*)(#.*)?$/);
    return m ? m[1].replace(/\s+$/, '') : l;
  });

  /** Find the line range of a top-level key block (indent === 0). */
  function blockRange(key) {
    const startRe = new RegExp(`^${key}\\s*:\\s*(.*)$`);
    let start = -1;
    let inlineValue = null;
    for (let i = 0; i < lines.length; i++) {
      const m = lines[i].match(startRe);
      if (m && !lines[i].startsWith(' ') && !lines[i].startsWith('\t')) {
        start = i;
        inlineValue = m[1];
        break;
      }
    }
    if (start === -1) return null;

    let end = lines.length;
    for (let i = start + 1; i < lines.length; i++) {
      const l = lines[i];
      if (l === '') continue;
      // next top-level key (no leading whitespace, contains ':')
      if (!/^\s/.test(l) && /^[A-Za-z_$][\w$-]*\s*:/.test(l)) {
        end = i;
        break;
      }
    }
    return { start, end, inlineValue };
  }

  /** Unquote a yaml scalar value (handles "…", '…', |, >, bare). */
  function unquote(v) {
    if (v == null) return '';
    let s = String(v).trim();
    if (s === '') return '';
    if (
      (s.startsWith('"') && s.endsWith('"')) ||
      (s.startsWith("'") && s.endsWith("'"))
    ) {
      return s.slice(1, -1);
    }
    return s;
  }

  /** Extract a top-level scalar field (single-line OR block scalar). */
  function scalar(key) {
    const r = blockRange(key);
    if (!r) return '';
    const head = r.inlineValue.trim();
    // block scalar?  `description: >-` / `>` / `|`
    if (head === '>' || head === '>-' || head === '|' || head === '|-') {
      const buf = [];
      for (let i = r.start + 1; i < r.end; i++) {
        const l = lines[i];
        if (l === '') {
          buf.push('');
          continue;
        }
        if (!/^\s/.test(l)) break;
        buf.push(l.replace(/^\s+/, ''));
      }
      const joined = buf.join(' ').replace(/\s+/g, ' ').trim();
      return joined;
    }
    return unquote(head);
  }

  out.name = scalar('name');
  out.tag = scalar('tag');
  out.component = scalar('component');
  out.description = scalar('description');

  /**
   * Parse a mapping-of-mappings block (props/slots/events).
   * Each child key is a 2-space-indented identifier, its value is a
   * 4-space-indented mapping of leaf scalars.
   */
  function mappingOfMappings(key) {
    const r = blockRange(key);
    if (!r) return {};
    const result = {};
    let currentKey = null;
    let currentField = null; // for block-scalar accumulation
    let currentBuf = null;
    let currentIndent = 0;

    const flushBlockScalar = () => {
      if (currentKey && currentField && currentBuf) {
        const joined = currentBuf.join(' ').replace(/\s+/g, ' ').trim();
        result[currentKey][currentField] = joined;
      }
      currentField = null;
      currentBuf = null;
    };

    for (let i = r.start + 1; i < r.end; i++) {
      const l = lines[i];
      if (l === '') continue;
      const indent = l.match(/^( *)/)[0].length;

      // 2-space child key:  "  propName:" or "  propName: value"
      if (indent === 2) {
        flushBlockScalar();
        const m = l.match(/^ {2}([^:\s]+)\s*:\s*(.*)$/);
        if (m) {
          currentKey = m[1];
          result[currentKey] = {};
          const v = m[2].trim();
          if (v && v !== '>' && v !== '>-' && v !== '|' && v !== '|-') {
            // shorthand "  prop: desc"
            result[currentKey].description = unquote(v);
          }
        }
        continue;
      }

      // 4-space leaf field:  "    description: …"
      if (indent === 4 && currentKey) {
        flushBlockScalar();
        const m = l.match(/^ {4}([^:\s]+)\s*:\s*(.*)$/);
        if (m) {
          const fk = m[1];
          const v = m[2].trim();
          if (v === '>' || v === '>-' || v === '|' || v === '|-') {
            currentField = fk;
            currentBuf = [];
            currentIndent = 6;
          } else {
            result[currentKey][fk] = unquote(v);
          }
        }
        continue;
      }

      // continuation of a block scalar (indent >= currentIndent)
      if (currentField && currentBuf && indent >= currentIndent) {
        currentBuf.push(l.slice(currentIndent));
        continue;
      }

      // 6-space (or deeper) list/enum entries we ignore for now, but
      // capture enum lists for props (best-effort).
      if (indent >= 6 && currentKey) {
        const em = l.match(/^\s+-\s+(.*)$/);
        if (em && currentField === 'enum') {
          currentBuf = currentBuf || [];
          // we don't really store enum lists here; left for the
          // manifest generator which re-parses with its own logic.
        }
      }
    }
    flushBlockScalar();
    return result;
  }

  try {
    out.props = mappingOfMappings('props');
  } catch (e) {
    out._errors.push(`props: ${e.message}`);
  }
  try {
    out.slots = mappingOfMappings('slots');
  } catch (e) {
    out._errors.push(`slots: ${e.message}`);
  }
  try {
    out.events = mappingOfMappings('events');
  } catch (e) {
    out._errors.push(`events: ${e.message}`);
  }

  return out;
}

/* ------------------------------------------------------------------ */
/*  Pretty-print helpers                                               */
/* ------------------------------------------------------------------ */
function wrap(s, width = 76, indent = '    ') {
  if (!s) return '';
  const words = String(s).split(/\s+/);
  const lines = [];
  let line = '';
  for (const w of words) {
    if ((line + ' ' + w).trim().length > width) {
      lines.push(line);
      line = w;
    } else {
      line = (line ? line + ' ' : '') + w;
    }
  }
  if (line) lines.push(line);
  return lines.map((l) => indent + l).join('\n');
}

function printSection(title, entries, keyLabel) {
  const keys = Object.keys(entries || {});
  if (keys.length === 0) return;
  console.log(`${title} (${keys.length})`);
  for (const k of keys) {
    const e = entries[k] || {};
    const meta = [];
    if (e.type) meta.push(e.type);
    if (e.default !== undefined && e.default !== '') meta.push(`default: ${e.default}`);
    const head = meta.length ? `  • ${k}  [${meta.join(', ')}]` : `  • ${k}`;
    console.log(head);
    if (e.description) console.log(wrap(e.description, 72, '      '));
  }
  console.log('');
}

function firstExampleFragment(md) {
  // First markdown ## …  block, or whole file if no headings.
  const lines = md.split(/\r?\n/);
  let start = -1;
  let end = lines.length;
  for (let i = 0; i < lines.length; i++) {
    if (/^##\s+/.test(lines[i])) {
      if (start === -1) {
        start = i;
      } else {
        end = i;
        break;
      }
    }
  }
  if (start === -1) return md.trim();
  return lines.slice(start, end).join('\n').trim();
}

function firstHtmlFragment(html) {
  // Heuristic: first <section> … </section>, else first <!-- example: --> … next, else first ~30 lines.
  const sect = html.match(/<section[\s\S]*?<\/section>/i);
  if (sect) return sect[0].trim();
  const lines = html.split(/\r?\n/);
  return lines.slice(0, 30).join('\n').trim();
}

/* ------------------------------------------------------------------ */
/*  Main                                                               */
/* ------------------------------------------------------------------ */
function listComponents() {
  let entries;
  try {
    entries = readdirSync(COMPONENTS_DIR);
  } catch (e) {
    console.error(`Cannot read components directory: ${COMPONENTS_DIR}`);
    process.exit(1);
  }
  const names = [];
  for (const d of entries) {
    let s;
    try { s = statSync(join(COMPONENTS_DIR, d)); } catch { continue; }
    if (!s.isDirectory()) continue;
    const yamlPath = join(COMPONENTS_DIR, d, `${d}.yaml`);
    if (existsSync(yamlPath)) names.push(d);
  }
  names.sort();
  console.log(`AdiaUI — ${names.length} components\n`);
  for (const c of names) console.log(`  ${c}`);
  console.log(`\nRun: npx @adia-ai/web-components doc <name>\n`);
}

const name = process.argv[2];
if (!name) {
  listComponents();
  process.exit(0);
}

const yamlPath = join(COMPONENTS_DIR, name, `${name}.yaml`);
if (!existsSync(yamlPath)) {
  console.error(`Component '${name}' not found in catalog.`);
  console.error(`Try: npx @adia-ai/web-components doc    # list all`);
  process.exit(1);
}

const source = readFileSync(yamlPath, 'utf-8');
const data = parseYaml(source);

console.log('');
console.log(`${data.name || name}  <${data.tag || name + '-ui'}>`);
console.log('─'.repeat(60));
if (data.description) {
  console.log(wrap(data.description, 76, ''));
  console.log('');
}

printSection('Props',  data.props,  'prop');
printSection('Slots',  data.slots,  'slot');
printSection('Events', data.events, 'event');

if (data._errors && data._errors.length) {
  console.log('Parse warnings:');
  for (const e of data._errors) console.log(`  - ${e}`);
  console.log('');
}

console.log(`Live demo: ${DEMO_BASE}/${name}`);
console.log('');

const examplesMdPath = join(COMPONENTS_DIR, name, `${name}.examples.md`);
const examplesHtmlPath = join(COMPONENTS_DIR, name, `${name}.examples.html`);
if (existsSync(examplesMdPath)) {
  const src = readFileSync(examplesMdPath, 'utf-8');
  console.log('Example');
  console.log('─'.repeat(60));
  console.log(firstExampleFragment(src));
  console.log('');
} else if (existsSync(examplesHtmlPath)) {
  const src = readFileSync(examplesHtmlPath, 'utf-8');
  console.log('Example (HTML)');
  console.log('─'.repeat(60));
  console.log(firstHtmlFragment(src));
  console.log('');
}
