import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { join } from "node:path";

export function createProjectPreferencesStore(dataDir) {
  const filePath = dataDir ? join(dataDir, "project-preferences.json") : "";
  const temporaryPath = dataDir ? join(dataDir, "project-preferences.json.tmp") : "";
  let mutationQueue = Promise.resolve();

  async function load() {
    if (!filePath) return { hiddenProjects: [] };
    try {
      const value = JSON.parse(await readFile(filePath, "utf8"));
      return { hiddenProjects: Array.isArray(value.hiddenProjects) ? value.hiddenProjects.filter(isHiddenProject) : [] };
    } catch (error) {
      if (error.code === "ENOENT") return { hiddenProjects: [] };
      throw error;
    }
  }

  async function save(state) {
    if (!filePath) return;
    await mkdir(dataDir, { recursive: true });
    await writeFile(temporaryPath, JSON.stringify(state, null, 2), "utf8");
    await rename(temporaryPath, filePath);
  }

  return {
    async hiddenProjects() {
      return (await load()).hiddenProjects;
    },
    async hide(projectKey, now = new Date()) {
      validateProjectKey(projectKey);
      const hiddenProject = { projectKey, hiddenAt: now.toISOString() };
      const operation = mutationQueue.then(async () => {
        const state = await load();
        state.hiddenProjects = state.hiddenProjects.filter((item) => item.projectKey !== projectKey);
        state.hiddenProjects.push(hiddenProject);
        await save(state);
        return hiddenProject;
      });
      mutationQueue = operation.catch(() => {});
      return await operation;
    }
  };
}

function validateProjectKey(value) {
  if (typeof value !== "string" || !value || value.length > 200 || /[\\/\u0000-\u001f]/.test(value)) {
    const error = new Error("invalid_project_key");
    error.status = 400;
    throw error;
  }
}

function isHiddenProject(value) {
  return value && typeof value.projectKey === "string" && typeof value.hiddenAt === "string" && Number.isFinite(Date.parse(value.hiddenAt));
}
