const fs = require("fs");
const { writeJsonAtomicSync } = require("../../../src/services/safe-files");
const { resolveData, ensureDataDirs } = require("./paths");
const { DEFAULT_SCOPE, normalizeScope } = require("./scope_manager");
const { DEFAULT_RATE_LIMITS, mergeLimits } = require("./rate_limits");
const { SOURCE_DEFAULTS } = require("./controller");

const DEFAULT_CONFIG = {
  enabled: false,
  selected_model_id: "qwen3-1.7b-q4",
  context_size: 4096,
  internal_generation_char_budget: 16000,
  threads: 0,
  gpu_allocation_intent_percent: 0,
  concurrency: 1,
  max_queue_length: 8,
  request_timeout_ms: 120000,
  ui_soft_timeout_ms: 45000,
  hard_generation_timeout_ms: 600000,
  max_output_tokens: 2048,
  output_budgets: {
    navigation_help: 256,
    simple_answer: 512,
    code_custom_command: 896,
    admin_debug: 1280,
    explicit_long: 2048
  },
  internal_mode_budgets: {
    fast: 1024,
    normal: 4096,
    expanded: 8192,
    unlimited: 16384
  },
  source_profiles: SOURCE_DEFAULTS,
  batch_size: 512,
  ubatch_size: 128,
  per_user_requests_per_minute: 6,
  admin_bypass_rate_limit: false,
  assistant_enabled: true,
  assistant_debug_logging: false,
  assistant_visibility: { admins: true, mods: false, users: false },
  work_history_retention: {
    mode: "count",
    count: 500,
    age_value: 30,
    age_unit: "days"
  },
  improvement: {
    allow_moderators_to_review_responses: false,
    trusted_moderator_reviewers: [],
    corrections_enabled: true
  },
  gate: {
    model_id: "smollm2-360m-q8",
    context_size: 1024,
    threads: 2,
    timeout_ms: 3000,
    high_confidence_threshold: 0.88,
    main_llm_threshold: 0.72,
    predefined_enabled: true,
    cache_ttl_seconds: 3600,
    repeat_force_window_seconds: 90,
    similarity_threshold: 0.86,
    force_prefix: "force ai:"
  },
  commands: {
    enabled: true,
    triggers: ["assistant", "lumi"],
    platforms: { discord: true, twitch: true, youtube: true, kick: false, other: false },
    roles: { admins: true, mods: true, users: true },
    unavailable_message: "Lumi Assistant is currently unavailable.",
    denied_message: "Lumi Assistant access is unavailable for your account."
  },
  rate_limits: DEFAULT_RATE_LIMITS,
  support_scope: DEFAULT_SCOPE,
  instructions: {
    out_of_scope_response: "I am sorry, but that is outside my scope.",
    roleplay_intensity: 0,
    community_tone: "",
    admin_custom: ""
  },
  logging: {
    log_prompts: false,
    log_responses: false,
    log_tool_calls: true,
    log_metrics: true,
    log_internal_audit: true
  }
};

function readJson(name, fallback) {
  ensureDataDirs();
  const file = resolveData("config", name);
  if (!fs.existsSync(file)) {
    writeJson(name, fallback);
    return structuredClone(fallback);
  }
  try { return { ...structuredClone(fallback), ...JSON.parse(fs.readFileSync(file, "utf8")) }; }
  catch { return structuredClone(fallback); }
}
function writeJson(name, value) {
  const file = resolveData("config", name);
  writeJsonAtomicSync(file, value);
}
function getConfig() {
  const config = readJson("ai_config.json", DEFAULT_CONFIG);
  if (config.gpu_workload_percent != null && config.gpu_allocation_intent_percent === DEFAULT_CONFIG.gpu_allocation_intent_percent) {
    config.gpu_allocation_intent_percent = Math.max(0, Math.min(100, Number(config.gpu_workload_percent) || 0));
  }
  delete config.gpu_workload_percent;
  config.support_scope = normalizeScope(config.support_scope || {
    allowed_topics: config.instructions?.allowed_topics,
    answer_style: config.instructions?.style,
    max_answer_length: config.instructions?.maximum_answer_length
  });
  config.assistant_visibility = { ...DEFAULT_CONFIG.assistant_visibility, ...(config.assistant_visibility || {}) };
  config.work_history_retention = mergeWorkHistoryRetention(config.work_history_retention);
  config.improvement = mergeImprovement(config.improvement);
  config.output_budgets = mergeOutputBudgets(config.output_budgets);
  config.internal_mode_budgets = mergeInternalModeBudgets(config.internal_mode_budgets);
  config.source_profiles = mergeSourceProfiles(config.source_profiles);
  config.gate = mergeGate(config.gate);
  config.instructions = { ...DEFAULT_CONFIG.instructions, ...(config.instructions || {}) };
  config.logging = { ...DEFAULT_CONFIG.logging, ...(config.logging || {}) };
  config.commands = mergeCommands(config.commands);
  config.rate_limits = mergeLimits(config.rate_limits);
  return config;
}
function saveConfig(value) {
  const merged = { ...DEFAULT_CONFIG, ...value };
  const legacyIntent = value.gpu_workload_percent;
  merged.gpu_allocation_intent_percent = Math.max(
    0,
    Math.min(100, Number(value.gpu_allocation_intent_percent ?? legacyIntent) || 0)
  );
  merged.internal_generation_char_budget = Math.max(
    2000,
    Math.min(64000, Number(value.internal_generation_char_budget) || DEFAULT_CONFIG.internal_generation_char_budget)
  );
  merged.ui_soft_timeout_ms = boundedNumber(
    value.ui_soft_timeout_ms,
    5000,
    300000,
    DEFAULT_CONFIG.ui_soft_timeout_ms
  );
  merged.hard_generation_timeout_ms = boundedNumber(
    value.hard_generation_timeout_ms ?? value.request_timeout_ms,
    30000,
    3600000,
    DEFAULT_CONFIG.hard_generation_timeout_ms
  );
  merged.max_output_tokens = boundedNumber(
    value.max_output_tokens,
    64,
    32768,
    DEFAULT_CONFIG.max_output_tokens
  );
  merged.output_budgets = mergeOutputBudgets(value.output_budgets);
  merged.internal_mode_budgets = mergeInternalModeBudgets(value.internal_mode_budgets);
  merged.source_profiles = mergeSourceProfiles(value.source_profiles);
  merged.batch_size = boundedNumber(value.batch_size, 32, 4096, DEFAULT_CONFIG.batch_size);
  merged.ubatch_size = Math.min(
    merged.batch_size,
    boundedNumber(value.ubatch_size, 16, 4096, DEFAULT_CONFIG.ubatch_size)
  );
  delete merged.gpu_workload_percent;
  merged.assistant_visibility = { ...DEFAULT_CONFIG.assistant_visibility, ...(value.assistant_visibility || {}) };
  merged.work_history_retention = mergeWorkHistoryRetention(value.work_history_retention);
  merged.improvement = mergeImprovement(value.improvement);
  merged.gate = mergeGate(value.gate);
  merged.support_scope = normalizeScope(value.support_scope);
  merged.instructions = { ...DEFAULT_CONFIG.instructions, ...(value.instructions || {}) };
  merged.logging = { ...DEFAULT_CONFIG.logging, ...(value.logging || {}) };
  merged.commands = mergeCommands(value.commands);
  merged.rate_limits = mergeLimits(value.rate_limits);
  writeJson("ai_config.json", merged);
  return merged;
}
function getRuntimeState() {
  return readJson("runtime_state.json", {
    desired_state: "stopped", last_known_state: "stopped", last_stop_reason: "never_started",
    last_manual_stop: true, last_crashed: false, last_exit_code: null,
    last_diagnostic_category: null, selected_model_id: null,
    gpu_allocation_actual_percent: 0, gpu_allocation_max_safe_percent: 0,
    gpu_allocation_clamped_reason: null, updated_at: new Date().toISOString()
  });
}
function saveRuntimeState(value) { writeJson("runtime_state.json", { ...value, updated_at: new Date().toISOString() }); }

function mergeCommands(value = {}) {
  return {
    ...DEFAULT_CONFIG.commands,
    ...value,
    platforms: { ...DEFAULT_CONFIG.commands.platforms, ...(value.platforms || {}) },
    roles: { ...DEFAULT_CONFIG.commands.roles, ...(value.roles || {}) },
    triggers: Array.isArray(value.triggers) && value.triggers.length
      ? value.triggers.map((entry) => String(entry).trim().replace(/^!+/, "").toLowerCase()).filter(Boolean)
      : [...DEFAULT_CONFIG.commands.triggers]
  };
}

function mergeGate(value = {}) {
  return {
    ...DEFAULT_CONFIG.gate,
    ...value,
    context_size: Math.max(512, Math.min(4096, Number(value.context_size) || DEFAULT_CONFIG.gate.context_size)),
    threads: Math.max(1, Math.min(16, Number(value.threads) || DEFAULT_CONFIG.gate.threads)),
    timeout_ms: boundedGateNumber(value.timeout_ms, 1000, 5000, DEFAULT_CONFIG.gate.timeout_ms),
    high_confidence_threshold: clampConfidence(value.high_confidence_threshold, DEFAULT_CONFIG.gate.high_confidence_threshold),
    main_llm_threshold: clampConfidence(value.main_llm_threshold, DEFAULT_CONFIG.gate.main_llm_threshold),
    cache_ttl_seconds: Math.max(30, Math.min(604800, Number(value.cache_ttl_seconds) || DEFAULT_CONFIG.gate.cache_ttl_seconds)),
    repeat_force_window_seconds: boundedGateNumber(
      value.repeat_force_window_seconds,
      0,
      3600,
      DEFAULT_CONFIG.gate.repeat_force_window_seconds
    ),
    similarity_threshold: clampConfidence(value.similarity_threshold, DEFAULT_CONFIG.gate.similarity_threshold),
    force_prefix: String(value.force_prefix ?? DEFAULT_CONFIG.gate.force_prefix).trim().slice(0, 40)
  };
}

function mergeOutputBudgets(value = {}) {
  return Object.fromEntries(
    Object.entries(DEFAULT_CONFIG.output_budgets).map(([key, fallback]) => [
      key,
      boundedNumber(value?.[key], 64, 32768, fallback)
    ])
  );
}

function mergeWorkHistoryRetention(value = {}) {
  const mode = value.mode === "age" ? "age" : "count";
  return {
    mode,
    count: boundedNumber(value.count, 50, 10000, DEFAULT_CONFIG.work_history_retention.count),
    age_value: boundedNumber(value.age_value, 1, 1000, DEFAULT_CONFIG.work_history_retention.age_value),
    age_unit: ["hours", "days", "weeks", "months", "years"].includes(value.age_unit)
      ? value.age_unit
      : DEFAULT_CONFIG.work_history_retention.age_unit
  };
}

function mergeInternalModeBudgets(value = {}) {
  return Object.fromEntries(
    Object.entries(DEFAULT_CONFIG.internal_mode_budgets).map(([key, fallback]) => [
      key,
      boundedNumber(value?.[key], 64, 32768, fallback)
    ])
  );
}

function mergeSourceProfiles(value = {}) {
  return Object.fromEntries(
    Object.entries(SOURCE_DEFAULTS).map(([source, fallback]) => {
      const incoming = value?.[source] || {};
      return [source, {
        source,
        hard_chars: incoming.hard_chars === null || incoming.hard_chars === undefined
          ? fallback.hard_chars
          : boundedNumber(incoming.hard_chars, 100, 12000, fallback.hard_chars || 1000),
        target_chars: boundedNumber(incoming.target_chars, 100, 12000, fallback.target_chars),
        allow_sections: incoming.allow_sections !== undefined ? incoming.allow_sections === true : fallback.allow_sections,
        allow_long_answer: incoming.allow_long_answer !== undefined ? incoming.allow_long_answer === true : fallback.allow_long_answer,
        allow_split: incoming.allow_split !== undefined ? incoming.allow_split === true : fallback.allow_split
      }];
    })
  );
}

function mergeImprovement(value = {}) {
  return {
    ...DEFAULT_CONFIG.improvement,
    ...value,
    allow_moderators_to_review_responses: value.allow_moderators_to_review_responses === true,
    corrections_enabled: value.corrections_enabled !== false,
    trusted_moderator_reviewers: [...new Set(
      (Array.isArray(value.trusted_moderator_reviewers) ? value.trusted_moderator_reviewers : [])
        .map((entry) => String(entry || "").trim())
        .filter(Boolean)
        .slice(0, 100)
    )]
  };
}

function clampConfidence(value, fallback) {
  const number = Number(value);
  return Number.isFinite(number) ? Math.max(0, Math.min(1, number)) : fallback;
}

function boundedGateNumber(value, min, max, fallback) {
  const number = Number(value);
  return Number.isFinite(number) ? Math.max(min, Math.min(max, number)) : fallback;
}

function boundedNumber(value, min, max, fallback) {
  const number = Number(value);
  return Number.isFinite(number) ? Math.max(min, Math.min(max, Math.round(number))) : fallback;
}

module.exports = { DEFAULT_CONFIG, getConfig, saveConfig, getRuntimeState, saveRuntimeState, readJson, writeJson };
