const fs = require("fs");
const path = require("path");
const { applyFilePolicy } = require("./file-policy");

const CORE_SKILL_NAMES = ["capability-router", "prismx-skill-gateway", "task-skill-router"];

/** CI / lightweight init — matrix task types still resolve when skills present */
const MINIMAL_BUNDLE_SKILLS = [
  "capability-router",
  "context-memory-bridge",
  "localization-hub",
  "refactor",
  "systematic-debugging"
];

const MINIMAL_BUNDLE_WORKFLOWS = ["forge.md"];

const BUNDLE_MODES = new Set(["full", "minimal"]);

const TEXT_EXTENSIONS = new Set([
  ".md",
  ".json",
  ".yaml",
  ".yml",
  ".txt",
  ".js",
  ".mjs",
  ".cjs",
  ".ts",
  ".tsx",
  ".jsx",
  ".css",
  ".html",
  ".htm",
  ".svg",
  ".xml",
  ".toml",
  ".sh",
  ".ps1",
  ".csv"
]);

function pkgRoot() {
  return path.join(__dirname, "..", "..", "..");
}

function loadTemplate(name) {
  const tplDir = path.join(pkgRoot(), "templates", name);
  const files = {};
  if (!fs.existsSync(tplDir)) return files;
  for (const rel of walk(tplDir)) {
    const key = rel.replace(/\\/g, "/");
    files[key] = fs.readFileSync(path.join(tplDir, rel), "utf-8");
  }
  return files;
}

function walk(dir, base = "") {
  const out = [];
  for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
    const rel = base ? `${base}/${ent.name}` : ent.name;
    if (ent.isDirectory()) out.push(...walk(path.join(dir, ent.name), rel));
    else out.push(rel);
  }
  return out;
}

function interpolate(content, vars) {
  let out = content;
  for (const [k, v] of Object.entries(vars)) {
    out = out.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v);
  }
  return out;
}

function listPackageSkillNames(root = pkgRoot()) {
  const skillsDir = path.join(root, "skills");
  if (!fs.existsSync(skillsDir)) return [];
  return fs
    .readdirSync(skillsDir)
    .filter((name) => fs.existsSync(path.join(skillsDir, name, "SKILL.md")))
    .sort();
}

function isTextBundleFile(filePath) {
  const ext = path.extname(filePath).toLowerCase();
  return !ext || TEXT_EXTENSIONS.has(ext);
}

function copyBinaryBundleFile({ rootDir, relativePath, srcAbs, force }) {
  const abs = path.join(rootDir, relativePath);
  const dir = path.dirname(abs);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });

  if (!fs.existsSync(abs)) {
    fs.copyFileSync(srcAbs, abs);
    return { path: relativePath, action: "created" };
  }

  if (force) {
    fs.copyFileSync(srcAbs, abs);
    return { path: relativePath, action: "updated", mode: "force" };
  }

  const existing = fs.readFileSync(abs);
  const incoming = fs.readFileSync(srcAbs);
  if (existing.equals(incoming)) {
    return { path: relativePath, action: "skipped", reason: "identical" };
  }

  const sidecar = `${relativePath}.capint.new`;
  fs.copyFileSync(srcAbs, path.join(rootDir, sidecar));
  return { path: relativePath, action: "conflict", sidecar };
}

function listProjectSkillNames(rootDir) {
  return listPackageSkillNames(rootDir);
}

function resolveBundleSkillNames(bundle, packageRoot = pkgRoot()) {
  if (bundle === "minimal") {
    return MINIMAL_BUNDLE_SKILLS.filter((name) =>
      fs.existsSync(path.join(packageRoot, "skills", name, "SKILL.md"))
    );
  }
  return listPackageSkillNames(packageRoot);
}

function normalizeBundleMode(mode) {
  const m = (mode || "full").toLowerCase();
  if (!BUNDLE_MODES.has(m)) return null;
  return m;
}

function deriveRegistryFromMatrix(matrix, packageRoot = pkgRoot(), options = {}) {
  const onDisk = new Set(options.skillNames || listPackageSkillNames(packageRoot));
  const core = new Set();
  const recommended = new Set();

  for (const name of CORE_SKILL_NAMES) {
    if (onDisk.has(name)) core.add(name);
  }
  if (!core.size) core.add("capability-router");

  for (const tt of matrix?.task_types || []) {
    for (const s of tt.skills || []) {
      if (core.has(s)) continue;
      if (onDisk.has(s)) recommended.add(s);
    }
  }
  for (const meta of Object.values(matrix?.rules_domain_map || {})) {
    for (const s of meta.skills || []) {
      if (core.has(s)) continue;
      if (onDisk.has(s)) recommended.add(s);
    }
  }

  for (const name of core) recommended.delete(name);

  const optional = [...onDisk].filter((name) => !core.has(name) && !recommended.has(name));

  return {
    schema_version: "1.1",
    core: [...core].sort(),
    recommended: [...recommended].sort(),
    optional,
    project_added: []
  };
}

function getPresetFiles(preset, vars = {}, options = {}) {
  const root = pkgRoot();
  const bundle = normalizeBundleMode(options.bundle) || "full";
  const matrixRaw = fs.readFileSync(path.join(root, "skill-routing-matrix.json"), "utf-8");
  const matrix = JSON.parse(matrixRaw);
  const skillNames = resolveBundleSkillNames(bundle, root);
  const registryJson = `${JSON.stringify(deriveRegistryFromMatrix(matrix, root, { skillNames }), null, 2)}\n`;
  const routingBundle = {
    "skill-routing-matrix.json": matrixRaw,
    "registry.json": registryJson
  };

  const presets = {
    minimal: () => ({ ...loadTemplate("minimal"), ...routingBundle }),
    "prismx-compatible": () => {
      const minimal = loadTemplate("minimal");
      const extra = loadTemplate("prismx-compatible");
      return { ...minimal, ...extra, ...routingBundle };
    }
  };
  const loader = presets[preset] || presets.minimal;
  const raw = loader();
  const files = {};
  for (const [rel, content] of Object.entries(raw)) {
    files[rel] = interpolate(content, vars);
  }
  return files;
}

function copyBundleTree({ rootDir, srcDir, prefix, force = false }) {
  const results = [];
  if (!fs.existsSync(srcDir)) return results;

  for (const rel of walk(srcDir)) {
    const relNorm = `${prefix}/${rel}`.replace(/\\/g, "/");
    const srcAbs = path.join(srcDir, rel);
    if (isTextBundleFile(srcAbs)) {
      const content = fs.readFileSync(srcAbs, "utf-8");
      results.push(
        applyFilePolicy({
          rootDir,
          relativePath: relNorm,
          content,
          force
        })
      );
    } else {
      results.push(copyBinaryBundleFile({ rootDir, relativePath: relNorm, srcAbs, force }));
    }
  }
  return results;
}

/** Copy skills/ + workflows/ into the target project (`full` default, `minimal` for CI). */
function copyBundle({ rootDir, force = false, bundle = "full" }) {
  const root = pkgRoot();
  const mode = normalizeBundleMode(bundle) || "full";

  if (mode === "minimal") {
    const results = [];
    for (const name of MINIMAL_BUNDLE_SKILLS) {
      const srcDir = path.join(root, "skills", name);
      if (!fs.existsSync(srcDir)) continue;
      results.push(
        ...copyBundleTree({ rootDir, srcDir, prefix: `skills/${name}`, force })
      );
    }
    for (const wf of MINIMAL_BUNDLE_WORKFLOWS) {
      const srcAbs = path.join(root, "workflows", wf);
      if (!fs.existsSync(srcAbs)) continue;
      results.push(
        applyFilePolicy({
          rootDir,
          relativePath: `workflows/${wf}`,
          content: fs.readFileSync(srcAbs, "utf-8"),
          force
        })
      );
    }
    return results;
  }

  return [
    ...copyBundleTree({ rootDir, srcDir: path.join(root, "skills"), prefix: "skills", force }),
    ...copyBundleTree({ rootDir, srcDir: path.join(root, "workflows"), prefix: "workflows", force })
  ];
}

const PRESET_MANIFEST = {
  minimal: {
    id: "minimal",
    description: "AGENT.md, design.md, rules, matrix, registry, skill bundle (full or --bundle minimal)",
    files: [
      "GUNLUK.md",
      "AGENT.md",
      "AGENTS.md",
      "design.md",
      "docs/kullanim-kilavuzu.md",
      "docs/conventions/daily-use.md",
      "docs/conventions/task-to-capability-cheatsheet.md",
      ".capint/rules/core.md",
      ".gitignore",
      "skill-routing-matrix.json",
      "registry.json",
      "skills/",
      "workflows/"
    ]
  },
  "prismx-compatible": {
    id: "prismx-compatible",
    description: "minimal + HANDOFF.md + .capint/context.json",
    extends: "minimal",
    files: ["HANDOFF.md", ".capint/context.json"]
  }
};

module.exports = {
  getPresetFiles,
  PRESET_MANIFEST,
  interpolate,
  deriveRegistryFromMatrix,
  copyBundle,
  listPackageSkillNames,
  listProjectSkillNames,
  resolveBundleSkillNames,
  normalizeBundleMode,
  MINIMAL_BUNDLE_SKILLS,
  MINIMAL_BUNDLE_WORKFLOWS,
  BUNDLE_MODES,
  pkgRoot
};
