import { randomUUID } from "node:crypto";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { setTimeout as defaultSleep } from "node:timers/promises";

const LOCK_FILE_NAME = ".alphapulse-profile.lock";
const DEFAULT_TIMEOUT_MS = 120_000;
const DEFAULT_STALE_MS = 30 * 60_000;
const DEFAULT_RETRY_MS = 250;

function defaultIsProcessAlive(pid) {
  if (!Number.isInteger(pid) || pid <= 0) {
    return false;
  }
  try {
    process.kill(pid, 0);
    return true;
  } catch (error) {
    return error?.code === "EPERM";
  }
}

async function readLockPayload(lockPath) {
  try {
    return JSON.parse(await readFile(lockPath, "utf8"));
  } catch {
    return null;
  }
}

function isStaleLock(payload, { now, staleMs, isProcessAlive }) {
  if (!payload || !Number.isInteger(payload.pid)) {
    return true;
  }
  const ageMs = now() - Number(payload.createdAt ?? 0);
  return ageMs > staleMs || !isProcessAlive(payload.pid);
}

export async function acquire1688ProfileLock(profileDir, options = {}) {
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
  const staleMs = options.staleMs ?? DEFAULT_STALE_MS;
  const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;
  const now = options.now ?? Date.now;
  const sleep = options.sleep ?? defaultSleep;
  const isProcessAlive = options.isProcessAlive ?? defaultIsProcessAlive;
  const pid = options.pid ?? process.pid;
  const token = options.token ?? randomUUID();
  const lockPath = path.join(profileDir, LOCK_FILE_NAME);
  const startedAt = now();

  await mkdir(profileDir, { recursive: true });

  while (now() - startedAt <= timeoutMs) {
    const payload = {
      pid,
      token,
      hostname: os.hostname(),
      createdAt: now(),
    };

    try {
      await writeFile(lockPath, JSON.stringify(payload, null, 2), { flag: "wx" });
      return {
        lockPath,
        token,
        release: async () => {
          const current = await readLockPayload(lockPath);
          if (current?.pid === pid && current?.token === token) {
            await rm(lockPath, { force: true });
          }
        },
      };
    } catch (error) {
      if (error?.code !== "EEXIST") {
        throw error;
      }
    }

    const existing = await readLockPayload(lockPath);
    if (isStaleLock(existing, { now, staleMs, isProcessAlive })) {
      await rm(lockPath, { force: true });
      continue;
    }

    await sleep(retryMs);
  }

  throw new Error(`1688 profile is locked: ${profileDir}`);
}
