const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const { PLUGIN_ROOT, resolveData } = require("./paths");
const { writeJsonAtomicSync } = require("../../../src/services/safe-files");

const INDEX_FILE = "index.json";
const REPOSITORY_URL = "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi";
const SOURCE_EXTENSIONS = new Set([".js", ".ejs", ".json", ".md"]);
const ROUTE_ALIASES = Object.freeze({
  "/admin/twitch-wizard": "twitch configuration config settings wizard location where integration",
  "/admin/discord-wizard": "discord configuration config settings wizard location where integration",
  "/admin/youtube-wizard": "youtube configuration config settings wizard location where integration",
  "/admin/plugins": "plugin plugins settings configuration management",
  "/admin/commands": "command commands custom command settings management",
  "/plugins/economy-framework": "economy currency points banking settings",
  "/plugins/moderation": "moderation mod tools sanctions bans timeouts settings"
});

function repoRoot() {
  return path.resolve(PLUGIN_ROOT, "..", "..");
}

function refreshIndex(root = repoRoot()) {
  const routes = [];
  const documents = [];
  const plugins = [];
  const commands = [];
  walk(root, (file) => {
    const relative = normalize(path.relative(root, file));
    if (relative.startsWith("plugins/lumi_ai/data/") || relative.startsWith("updates/") || relative.includes("/node_modules/")) return;
    const extension = path.extname(file).toLowerCase();
    if (!SOURCE_EXTENSIONS.has(extension)) return;
    const content = safeRead(file);
    if (!content) return;
    if (extension === ".js") collectRoutes(content, relative, routes);
    if (relative.endsWith("/plugin.json")) collectPlugin(content, relative, plugins);
    if (relative.endsWith("/cmds.json")) collectCommands(content, relative, commands);
    if (extension === ".md" || extension === ".ejs") {
      documents.push({ path: relative, title: firstTitle(content) || path.basename(file), summary: summarize(content) });
    }
  });
  const index = {
    schema_version: 1,
    source: "local",
    repository: REPOSITORY_URL,
    root: normalize(root),
    indexed_at: new Date().toISOString(),
    commit: readGitCommit(root),
    routes: uniqueBy(routes, (entry) => `${entry.method}:${entry.path}`),
    plugins: uniqueBy(plugins, (entry) => entry.id),
    commands: uniqueBy(commands, (entry) => `${entry.plugin}:${entry.name}`),
    documents
  };
  const target = resolveData("repo_index", INDEX_FILE);
  writeJsonAtomicSync(target, index);
  return index;
}

function refreshPublicIndex() {
  const staging = resolveData("repo_index", `public-${Date.now()}`);
  try {
    const result = spawnSync("git", ["clone", "--depth", "1", REPOSITORY_URL, staging], {
      encoding: "utf8",
      timeout: 120000,
      windowsHide: true,
      shell: false
    });
    if (result.status !== 0) {
      throw new Error((result.stderr || result.stdout || "Git clone failed.").trim().slice(0, 1000));
    }
    const index = refreshIndex(staging);
    index.source = "public";
    index.root = REPOSITORY_URL;
    writeJsonAtomicSync(resolveData("repo_index", INDEX_FILE), index);
    return index;
  } finally {
    const resolved = path.resolve(staging);
    const parent = path.resolve(resolveData("repo_index"));
    if (resolved.startsWith(`${parent}${path.sep}`)) fs.rmSync(resolved, { recursive: true, force: true });
  }
}

function loadIndex() {
  const target = resolveData("repo_index", INDEX_FILE);
  if (!fs.existsSync(target)) return null;
  try { return JSON.parse(fs.readFileSync(target, "utf8")); } catch { return null; }
}

function indexStatus(index = loadIndex()) {
  return index ? {
    present: true,
    indexed_at: index.indexed_at,
    commit: index.commit,
    route_count: index.routes?.length || 0,
    plugin_count: index.plugins?.length || 0,
    command_count: index.commands?.length || 0,
    stale: Date.now() - new Date(index.indexed_at).getTime() > 7 * 24 * 60 * 60 * 1000
  } : { present: false, indexed_at: null, commit: null, route_count: 0, plugin_count: 0, command_count: 0, stale: true };
}

function lookupSupport(message, index = loadIndex()) {
  if (!message) return null;
  const query = String(message).toLowerCase();
  const terms = query.split(/[^a-z0-9]+/).filter((term) => term.length > 2);
  if (isCommunityContactQuery(query)) {
    return {
      type: "contact",
      text: "Use the Discord community server to contact OokamiKunTV, Jenni, administrators, moderators, or community staff. I could not verify a Lumi WebUI, notification, direct-message, or Throne contact workflow."
    };
  }
  const ambiguous = /\b(this|that|it|option|setting|settings)\b/i.test(query) &&
    !/\b(lumi|bot|webui|plugin|twitch|discord|youtube|moderation|command)\b/i.test(query);
  if (ambiguous) {
    return {
      type: "clarification",
      text: "Do you mean a setting in Lumi Bot, or a setting on the external platform?"
    };
  }
  if (!index) {
    return isLumiFeatureQuery(query) ? {
      type: "unknown",
      text: "I could not verify that Lumi feature or WebUI location because the current repository and help index is unavailable. Which exact feature, plugin, or page do you mean?"
    } : null;
  }
  const candidates = augmentedRoutes(index)
    .filter((route) => route.method === "GET")
    .map((route) => ({ ...route, score: scoreRoute(route, terms) }))
    .filter((route) => route.score > 0)
    .sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
  if (!candidates.length || candidates[0].score < 2) {
    if (!isLumiFeatureQuery(query)) return null;
    return {
      type: "unknown",
      text: "I could not verify that Lumi feature or WebUI location in the current repository and help index. It may not exist or may use a different name. Which exact feature, plugin, or page do you mean?"
    };
  }
  const best = candidates[0];
  return {
    type: "route",
    text: `${routeLabel(best.path)} is available in Lumi's WebUI.`,
    links: [{ href: best.path, label: routeLabel(best.path) }],
    source: { path: best.source, confidence: best.score >= 5 ? "high" : "medium" }
  };
}

function isCommunityContactQuery(query) {
  const contact = /\b(contact|reach|message|dm|notify|notification|talk\s+to|send\s+(?:a\s+)?message)\b/i.test(query);
  const person = /\b(ookamikuntv|ookami|jenni|admin|admins|administrator|administrators|moderator|moderators|mods?|community\s+staff|staff)\b/i.test(query);
  return contact && person;
}

function isLumiFeatureQuery(query) {
  const lumiSubject = /\b(lumi|webui|plugin|plugins|feature|features|route|routes|page|pages|menu|setting|settings|command|commands|throne|notification|notifications|messaging|messages?|direct messages?|dm|inbox)\b/i.test(query);
  const featureIntent = /\b(where|find|open|navigate|location|located|does|is there|can lumi|how (?:do|can)|support|supports|have|has|enable|configure|setup|set up)\b/i.test(query);
  return lumiSubject && featureIntent;
}

function verifyInternalLinks(links, index = loadIndex()) {
  const known = new Set((index?.routes || []).filter((route) => route.method === "GET").map((route) => route.path));
  return (Array.isArray(links) ? links : []).filter((link) =>
    link && typeof link.href === "string" && link.href.startsWith("/") && known.has(link.href)
  );
}

function supportContext(message, index = loadIndex(), limit = 8, role = "user", allowModeratorCodeHelp = false) {
  if (typeof limit === "string") {
    allowModeratorCodeHelp = role === true;
    role = limit;
    limit = 8;
  }
  if (!index || !message) return [];
  const terms = String(message).toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 2);
  const technical = role === "admin";
  const snippetsAllowed = role === "mod" && allowModeratorCodeHelp;
  const rows = [
    ...augmentedRoutes(index).map((entry) => ({
      text: technical
        ? `Lumi route ${entry.path}; source ${entry.source || "curated route index"}`
        : `${routeLabel(entry.path)}: ${entry.path}`,
      value: `${entry.path} ${entry.source || ""} ${ROUTE_ALIASES[entry.path] || ""}`
    })),
    ...(index.plugins || []).map((entry) => ({
      text: technical
        ? `Plugin ${entry.name}: ${entry.description} (${entry.path})`
        : `Plugin ${entry.name}: ${entry.description}`,
      value: `${entry.name} ${entry.description} ${entry.id}`
    })),
    ...(index.commands || []).map((entry) => ({
      text: technical || snippetsAllowed
        ? `Command ${entry.name}: ${entry.description}${technical ? ` (${entry.plugin})` : ""}`
        : `Command ${entry.name}: ${entry.description}`,
      value: `${entry.name} ${entry.description} ${entry.plugin}`
    })),
    ...(technical ? index.documents || [] : []).map((entry) => ({
      text: `${entry.title}: ${entry.summary} (${entry.path})`,
      value: `${entry.title} ${entry.summary} ${entry.path}`
    }))
  ];
  return rows.map((row) => ({
    text: row.text,
    score: terms.reduce((score, term) => score + (row.value.toLowerCase().includes(term) ? 1 : 0), 0)
  })).filter((row) => row.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, limit)
    .map((row) => row.text);
}

function collectRoutes(content, source, output) {
  const pattern = /\b(?:app|router)\.(get|post|put|patch|delete)\(\s*["'`]([^"'`]+)["'`]/g;
  let match;
  while ((match = pattern.exec(content))) {
    if (!match[2].includes(":") && !match[2].includes("${")) {
      const pluginMatch = source.match(/^plugins\/([^/]+)\/index\.js$/);
      const routePath = pluginMatch && match[2].startsWith("/")
        ? `/plugins/${pluginMatch[1]}${match[2] === "/" ? "" : match[2]}`
        : match[2];
      output.push({ method: match[1].toUpperCase(), path: routePath, source });
    }
  }
}

function collectPlugin(content, source, output) {
  try {
    const manifest = JSON.parse(content);
    if (manifest.id) output.push({ id: manifest.id, name: manifest.name || manifest.id, description: manifest.description || "", path: source });
  } catch {}
}

function collectCommands(content, source, output) {
  try {
    const parsed = JSON.parse(content);
    const rows = Array.isArray(parsed) ? parsed : parsed.commands || [];
    for (const command of rows) {
      const name = command.name || command.command || command.trigger;
      if (name) output.push({ plugin: source.split("/")[1] || "core", name, description: command.description || "" });
    }
  } catch {}
}

function walk(dir, visit) {
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    if ([".git", "node_modules", "updates"].includes(entry.name)) continue;
    const target = path.join(dir, entry.name);
    if (entry.isDirectory()) walk(target, visit);
    else if (entry.isFile()) visit(target);
  }
}

function readGitCommit(root) {
  try {
    const git = path.join(root, ".git");
    const head = fs.readFileSync(path.join(git, "HEAD"), "utf8").trim();
    if (!head.startsWith("ref: ")) return head;
    return fs.readFileSync(path.join(git, head.slice(5)), "utf8").trim();
  } catch { return null; }
}

function scoreRoute(route, terms) {
  const haystack = `${route.path} ${route.source || ""} ${ROUTE_ALIASES[route.path] || ""}`.toLowerCase();
  const generic = new Set(["lumi", "webui", "setting", "settings", "admin", "page"]);
  return terms.reduce((score, term) => {
    if (!haystack.includes(term)) return score;
    if (route.path.includes(term) || ROUTE_ALIASES[route.path]?.includes(term)) return score + (generic.has(term) ? 1 : 4);
    return score + 1;
  }, 0);
}

function augmentedRoutes(index) {
  return [...(index?.routes || [])].filter(isUserFacingRoute);
}

function isUserFacingRoute(route) {
  if (!route || route.method !== "GET" || typeof route.path !== "string") return false;
  if (!route.path.startsWith("/")) return false;
  return !route.path.startsWith("/api/") && !route.path.includes("/api/");
}

function verifiedRoutePaths(index = loadIndex()) {
  return [...new Set(augmentedRoutes(index).map((route) => route.path))];
}

function routeLabel(route) {
  const known = {
    "/admin/twitch-wizard": "Twitch Configuration Wizard",
    "/admin/discord-wizard": "Discord Configuration Wizard",
    "/admin/youtube-wizard": "YouTube Configuration Wizard",
    "/admin/plugins": "Plugin Management",
    "/admin/commands": "Command Management",
    "/admin/settings": "Bot Settings"
  };
  const segment = route.split("/").filter(Boolean).pop() || "Lumi Home";
  return known[route] || segment.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
}

function firstTitle(content) {
  return content.match(/^\s*#\s+(.+)$/m)?.[1]?.trim() || content.match(/<h1[^>]*>([^<]+)<\/h1>/i)?.[1]?.trim();
}
function summarize(content) {
  return content.replace(/<[^>]+>/g, " ").replace(/[`#*_>\r\n]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 280);
}
function safeRead(file) {
  try { return fs.statSync(file).size <= 2 * 1024 * 1024 ? fs.readFileSync(file, "utf8") : ""; } catch { return ""; }
}
function normalize(value) { return value.split(path.sep).join("/"); }
function uniqueBy(items, key) { return [...new Map(items.map((item) => [key(item), item])).values()]; }

module.exports = {
  REPOSITORY_URL,
  refreshIndex,
  refreshPublicIndex,
  loadIndex,
  indexStatus,
  lookupSupport,
  supportContext,
  verifyInternalLinks,
  verifiedRoutePaths,
  isCommunityContactQuery,
  isLumiFeatureQuery,
  repoRoot
};
