import { randomUUID } from "node:crypto";
import { readdir, rename, rm } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";

const RESET_DATA_ENTRIES = [
  "sessions.json",
  "sessions.json.tmp",
  "attachments",
  "artifacts",
  "thread-preferences.json",
  "thread-preferences.json.tmp",
  "project-preferences.json",
  "project-preferences.json.tmp",
  "usage-history.json",
  "usage-history.json.tmp",
  "account-token-usage.json",
  "account-token-usage.json.tmp",
  "security"
];
const QUARANTINE_MARKER = ".crm-reset-";
const MAX_STALE_QUARANTINES = 32;

export async function deletePcApplicationData({ dataDir, logsDir }, dependencies = {}) {
  const move = dependencies.rename ?? rename;
  const remove = dependencies.rm ?? rm;
  const uuid = dependencies.randomUUID ?? randomUUID;
  const targets = await resetTargets(dataDir, logsDir, dependencies.readdir ?? readdir);
  await cleanupPcApplicationDataQuarantines({ dataDir, logsDir }, { ...dependencies, rm: remove });

  const staged = [];
  try {
    for (const target of targets) {
      const quarantine = `${target}${QUARANTINE_MARKER}${uuid()}`;
      try {
        await move(target, quarantine);
        staged.push({ target, quarantine });
      } catch (error) {
        if (error?.code !== "ENOENT") throw error;
      }
    }
  } catch {
    for (const item of staged.reverse()) await move(item.quarantine, item.target).catch(() => {});
    throw resetError("application_data_reset_failed", 500);
  }

  let pendingCleanup = 0;
  for (const item of staged) {
    try { await remove(item.quarantine, { recursive: true, force: true }); }
    catch { pendingCleanup += 1; }
  }
  return { deleted: true, pendingCleanup };
}

export async function cleanupPcApplicationDataQuarantines({ dataDir, logsDir }, dependencies = {}) {
  const read = dependencies.readdir ?? readdir;
  const remove = dependencies.rm ?? rm;
  const roots = uniquePaths([resolve(dataDir), dirname(resolve(dataDir)), dirname(resolve(logsDir))]);
  const stale = [];
  for (const root of roots) {
    const entries = await read(root, { withFileTypes: true }).catch((error) => {
      if (error?.code === "ENOENT") return [];
      throw error;
    });
    for (const entry of entries) {
      if (isResetQuarantine(entry.name)) stale.push(join(root, entry.name));
    }
  }
  if (stale.length > MAX_STALE_QUARANTINES) throw resetError("application_data_cleanup_required", 503);
  for (const path of stale) {
    try { await remove(path, { recursive: true, force: true }); }
    catch { throw resetError("application_data_cleanup_required", 503); }
  }
  return stale.length;
}

async function resetTargets(dataDir, logsDir, read) {
  const resolvedDataDir = resolveRequired(dataDir);
  const resolvedLogsDir = resolveRequired(logsDir);
  const entries = await read(resolvedDataDir, { withFileTypes: true }).catch((error) => error?.code === "ENOENT" ? [] : Promise.reject(error));
  const brokenSessions = entries
    .filter((entry) => entry.name.startsWith("sessions.json.broken.") && !entry.name.includes(QUARANTINE_MARKER))
    .map((entry) => join(resolvedDataDir, entry.name));
  return uniquePaths([
    ...RESET_DATA_ENTRIES.map((name) => join(resolvedDataDir, name)),
    ...brokenSessions,
    join(dirname(resolvedDataDir), "account-login-attempts"),
    resolvedLogsDir
  ]);
}

function isResetQuarantine(name) {
  const marker = name.indexOf(QUARANTINE_MARKER);
  if (marker <= 0) return false;
  const original = name.slice(0, marker);
  return RESET_DATA_ENTRIES.includes(original)
    || original === "account-login-attempts"
    || original === "logs"
    || original.startsWith("sessions.json.broken.");
}

function resolveRequired(value) {
  if (typeof value !== "string" || !value.trim()) throw resetError("application_data_reset_failed", 500);
  return resolve(value);
}

function uniquePaths(paths) {
  return [...new Set(paths)];
}

function resetError(code, status) {
  const error = new Error(code);
  error.status = status;
  error.safeLogCode = code;
  return error;
}
