#!/usr/bin/env node
/**
 * BrandLoop – Prompt-Seeding (idempotent, versioniert)
 *
 * Liest alle prompts/P*.md (Frontmatter + Body) und synchronisiert sie in die
 * Appwrite-Tabelle `prompt_templates`:
 *   - (key, model_adapter) ohne Zeile        → anlegen mit version 1
 *   - höchste Version inhaltsgleich          → skip (nur aktiv-Flag ggf. angleichen)
 *   - Inhalt geändert                        → neue Zeile version+1, alte aktiv=false
 *
 * Der Body ist das `static_core_md`. Die DB-Spalte `slots` (JSON-String) trägt
 * { name, slots[], modell_hinweis } – die Tabelle hat bewusst keine name-Spalte.
 *
 * Zusätzlich werden die 4 globalen Brand-Regeln aus dem Konzept in `rules`
 * geseedet (brand_id=null, Match per titel).
 *
 * Vorab-Check: Jeder {{SLOT}} im Body muss im Frontmatter deklariert sein und
 * umgekehrt – sonst Abbruch.
 */

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

const ENDPOINT = process.env.APPWRITE_ENDPOINT || 'https://appwrite.webklar.com/v1';
const PROJECT = process.env.APPWRITE_PROJECT || '6a5cee34002bb8360c34';
const ENV_FILE = process.env.ENV_FILE || '/home/webklar/apps/.env';
const DB_ID = 'brandloop';
const PROMPTS_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'prompts');

const API_KEY = process.env.APPWRITE_API_KEY
  || readFileSync(ENV_FILE, 'utf8').match(/^BRANDLOOP_APPWRITE_API_KEY=(.+)$/m)?.[1]?.trim();
if (!API_KEY) { console.error('Kein API-Key gefunden.'); process.exit(1); }

async function api(method, path, body) {
  const res = await fetch(`${ENDPOINT}${path}`, {
    method,
    headers: {
      'X-Appwrite-Project': PROJECT,
      'X-Appwrite-Key': API_KEY,
      'Content-Type': 'application/json',
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(`${method} ${path}: HTTP ${res.status} – ${json.message}`);
  return json;
}
const limit = (n) => `queries[]=${encodeURIComponent(JSON.stringify({ method: 'limit', values: [n] }))}`;

// ---- Prompt-Dateien einlesen ----------------------------------------------
function parseFrontmatter(raw, file) {
  const m = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
  if (!m) throw new Error(`${file}: kein Frontmatter gefunden`);
  const fm = {};
  for (const line of m[1].split('\n')) {
    const kv = line.match(/^([a-z_]+):\s*(.+)$/);
    if (!kv) continue;
    let value = kv[2].trim();
    if (value.startsWith('[')) value = value.slice(1, -1).split(',').map(s => s.trim()).filter(Boolean);
    else if (value === 'true') value = true;
    else if (value === 'false') value = false;
    fm[kv[1]] = value;
  }
  return { fm, body: m[2].trim() };
}

const files = readdirSync(PROMPTS_DIR).filter(f => /^P\d+.*\.md$/.test(f)).sort();
const templates = [];
let slotErrors = 0;

for (const file of files) {
  const { fm, body } = parseFrontmatter(readFileSync(join(PROMPTS_DIR, file), 'utf8'), file);
  const declared = new Set(Array.isArray(fm.slots) ? fm.slots : []);
  const used = new Set([...body.matchAll(/\{\{([A-Z_]+)\}\}/g)].map(x => x[1]));
  for (const s of used) if (!declared.has(s)) { console.error(`✗ ${file}: {{${s}}} im Body, aber nicht im Frontmatter deklariert`); slotErrors++; }
  for (const s of declared) if (!used.has(s)) { console.error(`✗ ${file}: Slot ${s} deklariert, aber nicht im Body verwendet`); slotErrors++; }
  templates.push({
    file,
    key: fm.key,
    model_adapter: fm.model_adapter ?? null,
    aktiv: fm.aktiv !== false,
    static_core_md: body,
    slots: JSON.stringify({ name: fm.name, slots: [...declared], modell_hinweis: fm.modell_hinweis ?? null }),
  });
}
if (slotErrors) { console.error(`\nAbbruch: ${slotErrors} Slot-Fehler.`); process.exit(1); }
console.log(`${templates.length} Prompt-Dateien gelesen, Slot-Check ok.\n`);

// ---- prompt_templates synchronisieren -------------------------------------
const existing = (await api('GET', `/tablesdb/${DB_ID}/tables/prompt_templates/rows?${limit(500)}`)).rows;
let created = 0, skipped = 0, superseded = 0;

for (const t of templates) {
  const family = existing.filter(r => r.key === t.key && (r.model_adapter ?? null) === t.model_adapter);
  const label = t.key + (t.model_adapter ? `/${t.model_adapter}` : '');
  const latest = family.sort((a, b) => b.version - a.version)[0];

  if (latest && latest.static_core_md === t.static_core_md && latest.slots === t.slots) {
    if (latest.aktiv !== t.aktiv) {
      await api('PATCH', `/tablesdb/${DB_ID}/tables/prompt_templates/rows/${latest.$id}`, { data: { aktiv: t.aktiv } });
      console.log(`~ ${label} v${latest.version}: aktiv → ${t.aktiv}`);
    } else {
      console.log(`= ${label} v${latest.version} (unverändert)`);
    }
    skipped++;
    continue;
  }

  const version = latest ? latest.version + 1 : 1;
  await api('POST', `/tablesdb/${DB_ID}/tables/prompt_templates/rows`, {
    rowId: 'unique()',
    data: { key: t.key, version, static_core_md: t.static_core_md, slots: t.slots, model_adapter: t.model_adapter, aktiv: t.aktiv },
  });
  console.log(`+ ${label} v${version}`);
  created++;
  for (const old of family.filter(r => r.aktiv)) {
    await api('PATCH', `/tablesdb/${DB_ID}/tables/prompt_templates/rows/${old.$id}`, { data: { aktiv: false } });
    console.log(`  - ${label} v${old.version} → aktiv=false`);
    superseded++;
  }
}

// ---- Globale Regeln seeden -------------------------------------------------
const GLOBAL_RULES = [
  { titel: 'Hintergrund clean', sortierung: 1, prompt_text:
    'Hintergründe neutral und aufgeräumt halten – kein visuelles Chaos, nichts, was vom Produkt ablenkt. (EN: clean, uncluttered background; nothing that distracts from the product.)' },
  { titel: 'Nur Verlangtes zeigen', sortierung: 2, prompt_text:
    'Nur zeigen, was die Szene verlangt – nicht mehr, nicht weniger. Keine ungefragten Extra-Elemente, Personen oder Requisiten. (EN: show only what is asked for — no unrequested extra elements, people, or props.)' },
  { titel: 'Logo-Schutz', sortierung: 3, prompt_text:
    'Das Brand-Logo nie verzerren, beschneiden, umfärben oder neu interpretieren – nur als freigegebenes Asset verwenden, nie generieren lassen. (EN: never distort, crop, recolor, or reinterpret the brand logo; use the released asset only, never generate it.)' },
  { titel: 'Referenzen nie kopieren', sortierung: 4, prompt_text:
    'Aus Referenzen fremder Inhalte nur beschriebene Merkmale lernen, nie 1:1 kopieren (Urheberrecht). Gesichter echter Personen nur mit dokumentierter Einwilligung – rein KI-generierte Gesichter sind der Standard. (EN: learn described features from references, never copy 1:1; real faces only with documented consent.)' },
];

const existingRules = (await api('GET', `/tablesdb/${DB_ID}/tables/rules/rows?${limit(100)}`)).rows;
let rulesCreated = 0;
for (const rule of GLOBAL_RULES) {
  if (existingRules.some(r => !r.brand_id && r.titel === rule.titel)) { console.log(`= Regel „${rule.titel}" (existiert)`); continue; }
  await api('POST', `/tablesdb/${DB_ID}/tables/rules/rows`, {
    rowId: 'unique()',
    data: { titel: rule.titel, prompt_text: rule.prompt_text, aktiv: true, sortierung: rule.sortierung },
  });
  console.log(`+ Regel „${rule.titel}"`);
  rulesCreated++;
}

console.log(`\nFertig: ${created} Prompt-Version(en) angelegt, ${skipped} unverändert, ${superseded} deaktiviert, ${rulesCreated} Regel(n) angelegt.`);
