const fs = require('fs');
const path = require('path');
const YAML = require('yaml');

const repoRoot = path.resolve(process.cwd(), '..');
const specRoot = path.join(repoRoot, 'pocket-base', 'spec');
const folders = ['openapi-wx', 'openapi-manage'];

const allFiles = [];
for (const folder of folders) {
  const dir = path.join(specRoot, folder);
  for (const name of fs.readdirSync(dir)) {
    if (name.endsWith('.yaml')) allFiles.push(path.join(dir, name));
  }
}

const docCache = new Map();
function loadDoc(filePath) {
  if (!docCache.has(filePath)) {
    const text = fs.readFileSync(filePath, 'utf8');
    docCache.set(filePath, YAML.parse(text));
  }
  return docCache.get(filePath);
}

function deepClone(v) {
  return v == null ? v : JSON.parse(JSON.stringify(v));
}

function decodePointer(seg) {
  return seg.replace(/~1/g, '/').replace(/~0/g, '~');
}

function getByPointer(doc, pointer) {
  if (!pointer || pointer === '#' || pointer === '') return doc;
  const parts = pointer.replace(/^#/, '').split('/').filter(Boolean).map(decodePointer);
  let cur = doc;
  for (const p of parts) {
    if (cur == null) return undefined;
    cur = cur[p];
  }
  return cur;
}

function mergeObjects(base, extra) {
  if (base && typeof base === 'object' && !Array.isArray(base) && extra && typeof extra === 'object' && !Array.isArray(extra)) {
    return { ...base, ...extra };
  }
  return base;
}

function isExternalRef(ref) {
  if (typeof ref !== 'string') return false;
  if (ref.startsWith('#')) return false;
  if (/^https?:\/\//i.test(ref)) return false;
  const [filePart] = ref.split('#');
  return !!filePart;
}

function resolveExternalRef(ref, baseFile) {
  const [filePart, hashPart = ''] = ref.split('#');
  const targetFile = path.resolve(path.dirname(baseFile), filePart);
  const targetDoc = loadDoc(targetFile);
  const pointer = hashPart ? `#${hashPart}` : '#';
  const targetNode = getByPointer(targetDoc, pointer);
  return { targetFile, targetNode: deepClone(targetNode) };
}

function derefExternals(node, baseFile, seen = new Set()) {
  if (Array.isArray(node)) {
    return node.map((item) => derefExternals(item, baseFile, seen));
  }
  if (!node || typeof node !== 'object') {
    return node;
  }

  if (typeof node.$ref === 'string' && isExternalRef(node.$ref)) {
    const key = `${baseFile}::${node.$ref}`;
    if (seen.has(key)) {
      return node;
    }
    seen.add(key);

    const { targetFile, targetNode } = resolveExternalRef(node.$ref, baseFile);
    let inlined = derefExternals(targetNode, targetFile, seen);

    const siblings = { ...node };
    delete siblings.$ref;
    if (Object.keys(siblings).length > 0) {
      inlined = mergeObjects(inlined, derefExternals(siblings, baseFile, seen));
    }
    return inlined;
  }

  const out = {};
  for (const [k, v] of Object.entries(node)) {
    out[k] = derefExternals(v, baseFile, seen);
  }
  return out;
}

function ensureTopLevel(doc, filePath) {
  if (!doc.openapi) doc.openapi = '3.1.0';
  if (!doc.info || typeof doc.info !== 'object') {
    const base = path.basename(filePath, '.yaml');
    const group = filePath.includes('openapi-wx') ? 'WX Native' : 'Manage Hooks';
    doc.info = {
      title: `BAI ${group} API - ${base}`,
      version: '1.0.0'
    };
  } else {
    if (!doc.info.title) doc.info.title = `BAI API - ${path.basename(filePath, '.yaml')}`;
    if (!doc.info.version) doc.info.version = '1.0.0';
  }
  if (!Array.isArray(doc.servers)) {
    doc.servers = [
      { url: 'https://bai-api.blv-oa.com', description: '生产环境' },
      { url: 'http://localhost:8090', description: 'PocketBase 本地环境' }
    ];
  }
}

for (const filePath of allFiles) {
  const original = loadDoc(filePath);
  const transformed = derefExternals(deepClone(original), filePath);
  ensureTopLevel(transformed, filePath);
  const outText = YAML.stringify(transformed, { lineWidth: 0 });
  fs.writeFileSync(filePath, outText, 'utf8');
  docCache.set(filePath, transformed);
}

console.log(`Processed ${allFiles.length} yaml files under openapi-wx/openapi-manage.`);
