// Open Design domain layer.
//
// Reads from the vendored corpus at workspace/skills/open-design/. The corpus
// is git-tracked text; this module exposes typed list/get APIs over it for
// both API routes and the agent-skills loader.
//
// The INDEX.md emitted by scripts/vendor-open-design.mjs contains a fenced
// JSON block with the canonical list of entries. We prefer that block as the
// source of truth and fall back to a filesystem walk when it is missing
// (e.g. when tests point WORKSPACE at a temp dir with a hand-crafted tree).

import fs from 'node:fs';
import path from 'node:path';
import { WORKSPACE } from './paths.js';

const REL_PATH = path.join('skills', 'open-design');

export function openDesignRoot() {
  return path.join(WORKSPACE, REL_PATH);
}

export function isVendored() {
  const root = openDesignRoot();
  try {
    return fs.existsSync(path.join(root, 'NOTICE.md'));
  } catch {
    return false;
  }
}

function readIndex() {
  const root = openDesignRoot();
  const indexPath = path.join(root, 'INDEX.md');
  if (!fs.existsSync(indexPath)) return null;
  const raw = fs.readFileSync(indexPath, 'utf8');
  const match = raw.match(/```json\n([\s\S]*?)\n```/);
  if (!match) return null;
  try {
    const parsed = JSON.parse(match[1]);
    if (parsed && Array.isArray(parsed.entries)) return parsed;
    return null;
  } catch {
    return null;
  }
}

function loadEntries() {
  const idx = readIndex();
  if (idx?.entries) return idx.entries;
  return [];
}

function withDirPath(entry) {
  const dir = path.dirname(entry.path);
  return { ...entry, dir };
}

export function listOpenDesignSkills() {
  return loadEntries().filter(e => e.kind === 'skill').map(withDirPath);
}

export function getOpenDesignSkill(slug) {
  if (!slug) return null;
  return loadEntries().filter(e => e.kind === 'skill').map(withDirPath).find(e => e.slug === slug) || null;
}

export function listDesignSystems() {
  return loadEntries().filter(e => e.kind === 'design-system').map(withDirPath);
}

export function getDesignSystem(slug) {
  if (!slug) return null;
  return loadEntries().filter(e => e.kind === 'design-system').map(withDirPath).find(e => e.slug === slug) || null;
}

export function listDesignTemplates() {
  return loadEntries().filter(e => e.kind === 'template').map(withDirPath);
}

export function getDesignTemplate(slug) {
  if (!slug) return null;
  return loadEntries().filter(e => e.kind === 'template').map(withDirPath).find(e => e.slug === slug) || null;
}

export function listCraftNotes() {
  return loadEntries().filter(e => e.kind === 'craft');
}

export function listPromptTemplates() {
  return loadEntries().filter(e => e.kind === 'prompt-template');
}

export function listAllEntries(opts = {}) {
  const entries = loadEntries().map(e => e.kind === 'craft' || e.kind === 'prompt-template' ? e : withDirPath(e));
  if (!opts.kind && !opts.scenario && !opts.query) return entries;
  const needle = opts.query ? String(opts.query).toLowerCase() : null;
  return entries.filter(entry => {
    if (opts.kind && entry.kind !== opts.kind) return false;
    if (opts.scenario && entry.scenario !== opts.scenario) return false;
    if (needle) {
      const hay = `${entry.slug} ${entry.title}`.toLowerCase();
      if (!hay.includes(needle)) return false;
    }
    return true;
  });
}

export function readEntryContents(entry) {
  if (!entry?.path) return null;
  const abs = path.join(openDesignRoot(), entry.path);
  try {
    return fs.readFileSync(abs, 'utf8');
  } catch {
    return null;
  }
}

export function readSkillContents(slug) {
  const entry = getOpenDesignSkill(slug);
  return entry ? readEntryContents(entry) : null;
}

export function getCommit() {
  const idx = readIndex();
  return idx?.commit || null;
}
