const fs = require("fs");
const crypto = require("crypto");
const { resolveData } = require("./paths");
const { roleAllows } = require("./permissions");
const { similarity, isSensitiveRequest } = require("./gate_provider");
const { atomicJson, paginate } = require("./feedback");

const PROMOTION_TARGETS = Object.freeze([
  "correction",
  "route_alias",
  "predefined_answer",
  "eval_case",
  "training_export"
]);

class CorrectionStore {
  constructor({ getConfig, verifyLink = () => false, file } = {}) {
    this.getConfig = getConfig || (() => ({}));
    this.verifyLink = verifyLink;
    this.file = file || resolveData("corrections", "corrections.json");
  }

  createFromFeedback(feedback, values, actor) {
    if (!feedback || feedback.status !== "approved") {
      throw new Error("Only approved feedback can be implemented.");
    }
    const target = PROMOTION_TARGETS.includes(values.target) ? values.target : "correction";
    const answer = clean(values.corrected_answer || feedback.optional_correction, 16000);
    if (!answer && target !== "eval_case") throw new Error("A corrected answer is required.");
    const expectedLink = clean(values.expected_link, 2000);
    this.validateLinks(answer, expectedLink);
    if (target === "route_alias" && !expectedLink) throw new Error("Route aliases require a verified Lumi route.");
    if (target === "predefined_answer" && isSensitiveRequest(feedback.user_message) && values.explicitly_safe !== true) {
      throw new Error("Sensitive or user-specific predefined answers must be explicitly marked safe.");
    }
    const entry = {
      id: crypto.randomUUID(),
      source_feedback_id: feedback.id,
      feedback_kind: feedback.feedback_kind || "strict_correction",
      feedback_tag: feedback.feedback_tag || "",
      prompt: feedback.user_message,
      corrected_answer: answer,
      rejected_answer: feedback.assistant_answer,
      target,
      route_alias: clean(values.route_alias, 500),
      expected_link: expectedLink,
      min_role: normalizeRole(values.min_role || feedback.role),
      permission_scope: {
        origin: clean(values.permission_origin || feedback.origin, 80) || "any",
        platform: clean(values.permission_platform || feedback.platform, 80) || "any"
      },
      explicitly_safe: values.explicitly_safe === true,
      enabled: values.enabled !== false,
      approved: true,
      approved_by: String(actor.id),
      approved_at: new Date().toISOString(),
      verified_by: null,
      verified_at: null,
      active: false,
      created_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
      linked_okf_path: null
    };
    const store = this.read();
    store.entries.unshift(entry);
    this.write(store);
    return entry;
  }

  list({ page = 1, pageSize = 20 } = {}) {
    return paginate(this.read().entries, page, pageSize);
  }

  all() {
    return this.read().entries;
  }

  get(id) {
    return this.read().entries.find((entry) => entry.id === id) || null;
  }

  update(id, values) {
    return this.mutate(id, (entry) => {
      const correctedAnswer = clean(values.corrected_answer ?? entry.corrected_answer, 16000);
      const expectedLink = clean(values.expected_link ?? entry.expected_link, 2000);
      this.validateLinks(correctedAnswer, expectedLink);
      if (entry.target === "route_alias" && !expectedLink) throw new Error("Route aliases require a verified Lumi route.");
      return {
        ...entry,
        corrected_answer: correctedAnswer,
        route_alias: clean(values.route_alias ?? entry.route_alias, 500),
        expected_link: expectedLink,
        min_role: normalizeRole(values.min_role || entry.min_role),
        permission_scope: {
          origin: clean(values.permission_origin ?? entry.permission_scope?.origin, 80) || "any",
          platform: clean(values.permission_platform ?? entry.permission_scope?.platform, 80) || "any"
        },
        explicitly_safe: values.explicitly_safe ?? entry.explicitly_safe,
        enabled: values.enabled ?? entry.enabled,
        active: false,
        updated_at: new Date().toISOString()
      };
    });
  }

  validateLinks(answer, expectedLink) {
    const links = [expectedLink, ...internalRouteReferences(answer)].filter(Boolean);
    if (links.some((link) => !this.verifyLink(link))) {
      throw new Error("Internal correction links must match a verified Lumi route.");
    }
  }

  verify(id, actor) {
    return this.mutate(id, (entry) => ({
      ...entry,
      approved: true,
      verified_by: String(actor.id),
      verified_at: new Date().toISOString(),
      active: false,
      updated_at: new Date().toISOString()
    }));
  }

  setEnabled(id, enabled) {
    return this.mutate(id, (entry) => ({
      ...entry,
      enabled: Boolean(enabled),
      active: false,
      updated_at: new Date().toISOString()
    }));
  }

  saveCorrections(actor) {
    const store = this.read();
    let active = 0;
    for (const entry of store.entries) {
      entry.active = Boolean(entry.enabled && entry.approved);
      entry.saved_by = String(actor.id);
      entry.saved_at = new Date().toISOString();
      if (entry.active) active += 1;
    }
    this.write(store);
    return { total: store.entries.length, active };
  }

  linkOkf(id, correctionPath) {
    return this.mutate(id, (entry) => ({
      ...entry,
      linked_okf_path: clean(correctionPath, 1000),
      updated_at: new Date().toISOString()
    }));
  }

  match(input, limit = 4) {
    if (this.getConfig()?.improvement?.corrections_enabled === false) return [];
    const role = normalizeRole(input.role);
    return this.read().entries
      .filter((entry) => entry.active && entry.enabled && entry.approved)
      .filter((entry) => roleAllows(role, entry.min_role))
      .filter((entry) => scopeAllows(entry.permission_scope, input))
      .map((entry) => ({ ...entry, score: similarity(entry.prompt, input.message) }))
      .filter((entry) => entry.score >= 0.45)
      .sort((left, right) => right.score - left.score)
      .slice(0, limit);
  }

  findPredefined(input) {
    return this.match(input, 10).find((entry) =>
      ["predefined_answer", "route_alias"].includes(entry.target) &&
      entry.explicitly_safe === true &&
      entry.score >= 0.82
    ) || null;
  }

  context(input, limit = 4) {
    return this.match(input, limit)
      .filter((entry) => ["correction", "route_alias", "predefined_answer"].includes(entry.target))
      .map((entry) => [
        `Reviewed correction for a similar request (minimum role: ${entry.min_role}):`,
        entry.feedback_kind === "instruction_based" || entry.feedback_tag === "wrong_tool_usage"
          ? `Revision guidance: ${entry.feedback_tag === "wrong_tool_usage" ? "tool-calling behavior" : "instruction"}`
          : "",
        `Request: ${entry.prompt}`,
        `Approved answer: ${entry.corrected_answer}`,
        entry.expected_link ? `Verified link: ${entry.expected_link}` : ""
      ].filter(Boolean).join("\n"));
  }

  delete(id) {
    const store = this.read();
    const before = store.entries.length;
    store.entries = store.entries.filter((entry) => entry.id !== id);
    if (before === store.entries.length) return false;
    this.write(store);
    return true;
  }

  mutate(id, updater) {
    const store = this.read();
    const index = store.entries.findIndex((entry) => entry.id === id);
    if (index < 0) throw new Error("Correction was not found.");
    store.entries[index] = updater(store.entries[index]);
    this.write(store);
    return store.entries[index];
  }

  read() {
    try {
      const parsed = JSON.parse(fs.readFileSync(this.file, "utf8"));
      return { entries: Array.isArray(parsed.entries) ? parsed.entries : [] };
    } catch {
      return { entries: [] };
    }
  }

  write(store) {
    atomicJson(this.file, { entries: store.entries.slice(0, 5000) });
  }
}

function scopeAllows(scope = {}, input = {}) {
  const originAllowed = !scope.origin || scope.origin === "any" || scope.origin === input.origin;
  const platformAllowed = !scope.platform || scope.platform === "any" || scope.platform === input.platform;
  return originAllowed && platformAllowed;
}

function normalizeRole(value) {
  return ["admin", "mod", "user"].includes(value) ? value : "user";
}

function clean(value, max) {
  return String(value || "").trim().slice(0, max);
}

function internalRouteReferences(value) {
  return [...String(value || "").matchAll(/(?:^|[\s("'`])((?:GET\s+)?\/[a-z0-9_./:-]+)/gi)]
    .map((match) => match[1].replace(/[.,;:!?]+$/, ""));
}

module.exports = { PROMOTION_TARGETS, CorrectionStore, scopeAllows, internalRouteReferences };
