// lib/skills/definitions.js — sole writer for skill marketplace rows + SKILL.md
// workspace files.
//
// upsertSkill() is the SINGLE write path for marketplace_items rows where
// kind='skill'. It validates the spec via skill-validator.js, writes the
// SKILL.md file under WORKSPACE/skills/<slug>/, and upserts the marketplace
// row + paired marketplace_installations entry. Provenance (created_by_*) is
// embedded inside manifest_json because marketplace_items has no dedicated
// provenance columns.
//
// archiveSkill() soft-deletes by setting installed=0 in marketplace_installations.
// getSkillDefinition() returns the marketplace_items row or null.

import { getDb, nowIso } from '../db.js';
import fs from 'node:fs';
import path from 'node:path';
import { WORKSPACE } from '../paths.js';
import { isInsideAllowedRoots } from '../tools/safety.js';
import { validateSkillSpec } from './skill-validator.js';

/**
 * Validate + upsert a skill spec.
 *
 * @param {object} spec  - Raw skill spec (slug, title, markdown, …)
 * @param {object} ctx   - Provenance context: { created_by_user_id, created_by_agent_id }
 * @returns {{ id, slug, invocation, file_path }}
 * @throws  Error with .code='spec_invalid' and .errors[] on validation failure.
 */
export function upsertSkill(spec, ctx = {}) {
  const result = validateSkillSpec(spec);
  if (!result.ok) {
    const e = new Error('skill spec invalid');
    e.code = 'spec_invalid';
    e.errors = result.errors;
    throw e;
  }

  const normalized = result.normalized;
  const { slug, title, description, category, invocation, markdown, trigger_examples } = normalized;
  const id = `skill:${slug}`;
  const filePath = `skills/${slug}/SKILL.md`;

  // Build the manifest that lib/marketplace.js reads.
  // Provenance embedded here because marketplace_items has no dedicated columns.
  const manifest = {
    invocation,
    file_paths: [filePath],
    trigger_examples,
    source_markdown: markdown,
    created_by_user_id: ctx.created_by_user_id || null,
    created_by_agent_id: ctx.created_by_agent_id || null,
  };

  // Write the SKILL.md file under WORKSPACE. The path is CONSTRUCTED from a
  // validated slug (no traversal possible), so assert containment with
  // isInsideAllowedRoots — not resolveSafePath, whose system-dir block would
  // false-trip when WORKSPACE legitimately lives under /private or /var.
  const target = path.join(WORKSPACE, 'skills', slug, 'SKILL.md');
  if (!isInsideAllowedRoots(target)) {
    throw new Error(`Refusing to write skill file outside the workspace: ${target}`);
  }
  fs.mkdirSync(path.dirname(target), { recursive: true });
  fs.writeFileSync(target, markdown, 'utf8');

  const db = getDb();
  const now = nowIso();
  const author = ctx.created_by_agent_id ? 'tool-smith' : 'madarx';
  const manifestJson = JSON.stringify(manifest);
  const tagsJson = JSON.stringify([category, 'skill']);

  const existing = db
    .prepare("SELECT id FROM marketplace_items WHERE kind='skill' AND slug=?")
    .get(slug);

  if (existing) {
    db.prepare(`
      UPDATE marketplace_items
         SET title=?, description=?, category=?, author=?, source_kind=?,
             version=?, manifest_json=?, risk_summary=?, tags=?
       WHERE kind='skill' AND slug=?
    `).run(
      title,
      description,
      category,
      author,
      'authored',
      '1.0',
      manifestJson,
      'Declarative skill — instructions only, no code executes.',
      tagsJson,
      slug
    );
  } else {
    db.prepare(`
      INSERT INTO marketplace_items
        (id, kind, slug, title, description, category, author, source_kind,
         version, manifest_json, risk_summary, tags, created_at)
      VALUES (?, 'skill', ?, ?, ?, ?, ?, 'authored', '1.0', ?, ?, ?, ?)
    `).run(
      id,
      slug,
      title,
      description,
      category,
      author,
      manifestJson,
      'Declarative skill — instructions only, no code executes.',
      tagsJson,
      now
    );

    // Paired installations row — installed=0 (not yet installed by default).
    db.prepare(`
      INSERT OR IGNORE INTO marketplace_installations (item_id, installed)
      VALUES (?, 0)
    `).run(id);
  }

  return { id, slug, invocation, file_path: filePath };
}

/**
 * Soft-archive a skill by setting installed=0 in marketplace_installations.
 * The marketplace_items row and SKILL.md file are preserved.
 *
 * @param {string} slug
 * @returns {{ ok: boolean, slug: string, error?: string }}
 */
export function archiveSkill(slug) {
  if (!slug) return { ok: false, error: 'not_found', slug };
  const db = getDb();
  const id = `skill:${slug}`;
  const row = db
    .prepare("SELECT id FROM marketplace_items WHERE kind='skill' AND slug=?")
    .get(slug);
  if (!row) return { ok: false, error: 'not_found', slug };

  db.prepare(`
    UPDATE marketplace_installations SET installed=0 WHERE item_id=?
  `).run(id);

  return { ok: true, slug };
}

/**
 * Return the marketplace_items row for the given skill slug, or null.
 *
 * @param {string} slug
 * @returns {object|null}
 */
export function getSkillDefinition(slug) {
  if (!slug) return null;
  return getDb()
    .prepare("SELECT * FROM marketplace_items WHERE kind='skill' AND slug=? LIMIT 1")
    .get(slug) || null;
}
