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

const STEP_FIELDS = ["tag", "type", "id", "name", "className", "dataTestId", "role", "text", "ariaLabel", "title", "hrefPath", "frameHint", "selector"];
const FORM_FIELD_FIELDS = ["key", "selector", "frameHint", "tag", "type", "id", "name", "dataTestId", "ariaLabel", "placeholder", "labelText"];

function normalizeInsurerId(insurer) {
  const normalized = String(insurer || "").trim().toLowerCase();
  if (!/^[a-z0-9-]{2,40}$/.test(normalized)) throw new Error("Nieprawidłowy identyfikator towarzystwa.");
  return normalized;
}

function normalizeStep(rawStep) {
  const step = {};
  for (const field of STEP_FIELDS) {
    const maxLength = field === "selector" ? 500 : field === "hrefPath" || field === "frameHint" ? 220 : 160;
    step[field] = String(rawStep?.[field] || "").replace(/\s+/g, " ").trim().slice(0, maxLength);
  }
  return step.text || step.ariaLabel || step.title || step.id || step.name || step.hrefPath ? step : null;
}

function normalizeFormField(rawField) {
  const field = {};
  for (const name of FORM_FIELD_FIELDS) {
    const maxLength = name === "selector" ? 500 : name === "labelText" ? 240 : name === "frameHint" ? 220 : 200;
    field[name] = String(rawField?.[name] || "").replace(/\s+/g, " ").trim().slice(0, maxLength);
  }
  return field.key && field.selector ? field : null;
}

function normalizeFormFields(fields) {
  return (Array.isArray(fields) ? fields.slice(0, 20) : []).map(normalizeFormField).filter(Boolean);
}

function normalizePath(path) {
  return (Array.isArray(path) ? path.slice(0, 60) : []).map(normalizeStep).filter(Boolean);
}

function normalizeRoute(route, insurer) {
  return {
    version: 1,
    insurer,
    updatedAt: typeof route?.updatedAt === "string" ? route.updatedAt : new Date().toISOString(),
    description: String(route?.description || `Trasa automatyzacji dla ${insurer}`).replace(/\s+/g, " ").trim().slice(0, 240),
    formPath: normalizePath(route?.formPath),
    formFields: normalizeFormFields(route?.formFields),
    resultPath: normalizePath(route?.resultPath),
  };
}

export function createPortableRouteStore({ storageDirectory, defaultsDirectory = "" }) {
  function getRouteFilePath(insurer) {
    return join(storageDirectory, `${normalizeInsurerId(insurer)}.json`);
  }

  function getPreviousRouteFilePath(insurer) {
    return join(storageDirectory, `${normalizeInsurerId(insurer)}.poprzednia.json`);
  }

  function getDefaultRouteFilePath(insurer) {
    if (!defaultsDirectory) return "";
    return join(defaultsDirectory, `${normalizeInsurerId(insurer)}.json`);
  }

  function parseRouteFile(contents, insurer, fileLabel) {
    try {
      const parsed = JSON.parse(contents);
      if (parsed?.version !== 1 || parsed?.insurer !== insurer) {
        throw new Error("Niezgodny format pliku trasy.");
      }
      return normalizeRoute(parsed, insurer);
    } catch (error) {
      throw new Error(`Plik trasy ${fileLabel} jest nieprawidłowy: ${error?.message || "błąd JSON"}`);
    }
  }

  async function readRouteFile(filePath, insurer, fileLabel) {
    let contents;
    try {
      contents = await readFile(filePath, "utf8");
    } catch (error) {
      if (error?.code === "ENOENT") return null;
      throw error;
    }
    return parseRouteFile(contents, insurer, fileLabel);
  }

  async function writeRouteFileAtomically(filePath, insurer, route) {
    await mkdir(storageDirectory, { recursive: true });
    const temporaryPath = join(storageDirectory, `${insurer}.${randomUUID()}.tmp`);
    try {
      await writeFile(temporaryPath, `${JSON.stringify(route, null, 2)}\n`, "utf8");
      await rename(temporaryPath, filePath);
    } finally {
      await rm(temporaryPath, { force: true }).catch(() => {});
    }
  }

  async function loadRoute(insurer) {
    const normalizedInsurer = normalizeInsurerId(insurer);
    return readRouteFile(getRouteFilePath(normalizedInsurer), normalizedInsurer, `${normalizedInsurer}.json`);
  }

  async function loadPreviousRoute(insurer) {
    const normalizedInsurer = normalizeInsurerId(insurer);
    return readRouteFile(getPreviousRouteFilePath(normalizedInsurer), normalizedInsurer, `${normalizedInsurer}.poprzednia.json`)
      .catch(() => null);
  }

  async function loadDefaultRoute(insurer) {
    const normalizedInsurer = normalizeInsurerId(insurer);
    const defaultPath = getDefaultRouteFilePath(normalizedInsurer);
    if (!defaultPath) return null;
    return readRouteFile(defaultPath, normalizedInsurer, `domyślnej trasy ${normalizedInsurer}.json`)
      .catch(() => null);
  }

  async function saveRoute(insurer, route, { backup = true } = {}) {
    const normalizedInsurer = normalizeInsurerId(insurer);
    const normalizedRoute = normalizeRoute({ ...route, updatedAt: new Date().toISOString() }, normalizedInsurer);
    const targetPath = getRouteFilePath(normalizedInsurer);
    if (backup) {
      const currentRoute = await readRouteFile(targetPath, normalizedInsurer, `${normalizedInsurer}.json`).catch(() => null);
      if (currentRoute) {
        await writeRouteFileAtomically(getPreviousRouteFilePath(normalizedInsurer), normalizedInsurer, currentRoute);
      }
    }
    await writeRouteFileAtomically(targetPath, normalizedInsurer, normalizedRoute);
    return normalizedRoute;
  }

  async function restoreRoute(insurer) {
    const normalizedInsurer = normalizeInsurerId(insurer);
    const activePath = getRouteFilePath(normalizedInsurer);
    const previousPath = getPreviousRouteFilePath(normalizedInsurer);
    const activeRoute = await readRouteFile(activePath, normalizedInsurer, `${normalizedInsurer}.json`).catch(() => null);
    const previousRoute = await loadPreviousRoute(normalizedInsurer);
    if (previousRoute) {
      await writeRouteFileAtomically(activePath, normalizedInsurer, previousRoute);
      if (activeRoute) {
        await writeRouteFileAtomically(previousPath, normalizedInsurer, activeRoute);
      } else {
        await rm(previousPath, { force: true });
      }
      return { source: "poprzednia", route: previousRoute };
    }
    const defaultRoute = await loadDefaultRoute(normalizedInsurer);
    if (defaultRoute) {
      if (activeRoute) {
        await writeRouteFileAtomically(previousPath, normalizedInsurer, activeRoute);
      }
      await writeRouteFileAtomically(activePath, normalizedInsurer, defaultRoute);
      return { source: "domyslna", route: defaultRoute };
    }
    throw new Error("Brak zapisanej poprzedniej trasy i trasy domyślnej do przywrócenia.");
  }

  async function seedDefaultRoutes() {
    if (!defaultsDirectory) return [];
    let defaultFiles;
    try {
      defaultFiles = await readdir(defaultsDirectory);
    } catch {
      return [];
    }
    const seededInsurers = [];
    for (const fileName of defaultFiles) {
      const match = fileName.match(/^([a-z0-9-]{2,40})\.json$/);
      if (!match) continue;
      const insurer = match[1];
      const activeRoute = await readRouteFile(getRouteFilePath(insurer), insurer, `${insurer}.json`).catch(() => null);
      if (activeRoute) continue;
      const defaultRoute = await loadDefaultRoute(insurer);
      if (!defaultRoute) continue;
      await writeRouteFileAtomically(getRouteFilePath(insurer), insurer, defaultRoute);
      seededInsurers.push(insurer);
    }
    return seededInsurers;
  }

  async function deleteRoute(insurer) {
    const normalizedInsurer = normalizeInsurerId(insurer);
    
    
    
    
    const activeRoute = await readRouteFile(
      getRouteFilePath(normalizedInsurer),
      normalizedInsurer,
      `${normalizedInsurer}.json`,
    ).catch(() => null);
    if (activeRoute) {
      await writeRouteFileAtomically(getPreviousRouteFilePath(normalizedInsurer), normalizedInsurer, activeRoute);
    }
    await rm(getRouteFilePath(normalizedInsurer), { force: true });
  }

  return {
    deleteRoute,
    getRouteFilePath,
    loadDefaultRoute,
    loadPreviousRoute,
    loadRoute,
    restoreRoute,
    saveRoute,
    seedDefaultRoutes,
  };
}
