#!/usr/bin/env node
import fs from 'fs';
import os from 'os';
import path from 'path';
import { fileURLToPath } from 'url';

import {
  VL_VERSION,
  THEME_VERSION,
  THEME_VERSION_FULL,
  STYLESPACE_VERSION,
  WORKFLOW_SPEC_DOC_VERSION,
} from '../src/data/versions.js';
import { DOCCENTER_API_URL } from '../src/data/doc-paths.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const APPLY = process.argv.includes('--apply');
const AUTH_PATH = path.join(os.homedir(), '.vl-code', 'auth.json');

const cookie = process.env.DOCCENTER_COOKIE || (() => {
  try {
    return JSON.parse(fs.readFileSync(AUTH_PATH, 'utf-8')).cookie;
  } catch {
    return '';
  }
})();

if (!cookie) {
  console.error('Missing DocCenter cookie. Set DOCCENTER_COOKIE or login so ~/.vl-code/auth.json exists.');
  process.exit(1);
}

const headers = {
  'Content-Type': 'application/json',
  Cookie: String(cookie).startsWith('ih5bearer=') ? String(cookie) : `ih5bearer=${cookie}`,
};

async function dcPost(endpoint, body) {
  const res = await fetch(`${DOCCENTER_API_URL}/${endpoint}`, {
    method: 'POST',
    headers,
    body: JSON.stringify(body),
  });
  const text = await res.text();
  let payload = {};
  try {
    payload = text ? JSON.parse(text) : {};
  } catch {
    throw new Error(`DocCenter returned non-JSON for ${endpoint}: ${text.slice(0, 300)}`);
  }
  if (!res.ok) {
    throw new Error(payload?.error || payload?.message || `DocCenter ${endpoint} failed: ${res.status}`);
  }
  return payload;
}

async function loadCatalog() {
  const result = await dcPost('SERVICE_DocCenter_GetDocList', {
    keyword: '',
    tagId: 0,
    page: 1,
    pageSize: 300,
  });
  return Array.isArray(result?.data) ? result.data : [];
}

async function getDocById(docId) {
  const result = await dcPost('SERVICE_DocCenter_GetDocById', { docId });
  return result?.data || null;
}

function normalizeSyntaxContent(content) {
  return String(content || '')
    .replace(/Current version:\s*`\/\/ VL_VERSION:[\d.]+`/g, `Current version: \`// VL_VERSION:${VL_VERSION}\``)
    .replace(/\/\/ VL_VERSION:[\d.]+/g, `// VL_VERSION:${VL_VERSION}`)
    .replace(/THEME_[\d.]+\.vth/g, `THEME_${THEME_VERSION}.vth`)
    .replace(/Theme-Enterprise-[\d.]+/g, `Theme-Enterprise-${THEME_VERSION}`);
}

function normalizeThemeContent(content) {
  return String(content || '')
    .replace(/VL_VERSION:[\d.]+/g, `VL_VERSION:${VL_VERSION}`)
    .replace(/Theme-Enterprise-[\d.]+/g, `Theme-Enterprise-${THEME_VERSION}`)
    .replace(/version:"[\d.]+"/g, `version:"${THEME_VERSION_FULL}"`)
    .replace(/styleSpaceVersion:"[\d.]+"/g, `styleSpaceVersion:"${STYLESPACE_VERSION}"`);
}

async function buildManifest(catalog) {
  const syntaxDoc = catalog.find((doc) => String(doc.path) === '1');
  const syntaxRemote = syntaxDoc ? await getDocById(syntaxDoc._id || syntaxDoc.id) : null;

  return [
    {
      path: '1',
      name: `VL_${VL_VERSION}.md`,
      description: `Canonical VL syntax reference for VL ${VL_VERSION}.`,
      currentContent: normalizeSyntaxContent(
        syntaxRemote?.currentContent || fs.readFileSync(path.join(ROOT, 'src', 'data', 'vl-syntax.md'), 'utf-8')
      ),
      changeNote: `v${VL_VERSION} — align syntax reference with Doc ID based official document resolution and THEME ${THEME_VERSION}`,
    },
    {
      path: '2',
      name: `THEME_${THEME_VERSION}.vth`,
      description: `Canonical enterprise theme for VL ${VL_VERSION} / THEME ${THEME_VERSION}.`,
      currentContent: normalizeThemeContent(
        fs.readFileSync(path.join(ROOT, 'public', 'seed-theme', 'Theme.vth'), 'utf-8')
      ),
      changeNote: `v${THEME_VERSION} — align Theme root tag, VL version, and enterprise seed with official Doc ID configuration`,
    },
    {
      path: '3',
      name: `VL Workflow Spec ${WORKFLOW_SPEC_DOC_VERSION}`,
      description: 'Canonical workflow specification with Doc ID priority, Tool_* runtime semantics, and checkpoint rerun compatibility.',
      currentContent: fs.readFileSync(path.join(ROOT, 'docs', `vl-workflow-spec-${WORKFLOW_SPEC_DOC_VERSION}.md`), 'utf-8'),
      changeNote: `v${WORKFLOW_SPEC_DOC_VERSION} — clarify Doc ID as persisted workflow binding, keep path as slot alias, and align workflow runtime compatibility`,
    },
    {
      path: '4',
      name: 'VL Metadata Spec',
      description: 'Canonical VL ProjectMeta schema for normalization, diff, workflow regeneration, and IDE tooling.',
      currentContent: fs.readFileSync(path.join(ROOT, 'docs', 'vl-metadata-spec-3.1.md'), 'utf-8'),
      changeNote: 'v3.1 — clarify Doc ID bindings stay outside ProjectMeta while keeping canonical schema and theme slot metadata rules',
    },
  ];
}

async function ensureDoc(entry, catalog) {
  let doc = catalog.find((item) => String(item.path) === String(entry.path));
  if (doc) return doc;
  if (!APPLY) {
    return null;
  }
  const created = await dcPost('SERVICE_DocCenter_CreateDoc', {
    name: entry.name,
    description: entry.description,
    path: entry.path,
  });
  doc = created?.data || created;
  catalog.push({
    _id: doc?._id || doc?.id || doc?.docId,
    id: doc?.id,
    path: entry.path,
    name: entry.name,
  });
  return catalog.find((item) => String(item.path) === String(entry.path)) || doc;
}

async function publishEntry(entry, catalog) {
  const doc = await ensureDoc(entry, catalog);
  const docId = doc?._id || doc?.id || null;
  const remote = docId ? await getDocById(docId) : null;
  const remoteContent = remote?.currentContent || '';
  const action = remoteContent === entry.currentContent ? 'unchanged' : (APPLY ? 'published' : 'publish');

  if (APPLY && remoteContent !== entry.currentContent) {
    await dcPost('SERVICE_DocCenter_SaveAsVersion', {
      path: entry.path,
      name: entry.name,
      description: entry.description,
      currentContent: entry.currentContent,
      changeNote: entry.changeNote,
    });
  }

  return {
    action,
    path: entry.path,
    name: entry.name,
    docId: docId || '(new)',
  };
}

async function main() {
  const catalog = await loadCatalog();
  const manifest = await buildManifest(catalog);

  console.log(`\nDocCenter core-doc publish ${APPLY ? '(apply)' : '(dry-run)'}`);
  for (const entry of manifest) {
    const result = await publishEntry(entry, catalog);
    const label = result.action.padEnd(9, ' ');
    console.log(`${label} path ${result.path}  ${result.name}  (docId ${result.docId})`);
  }
}

main().catch((err) => {
  console.error(err.message);
  process.exit(1);
});
