import crypto from 'node:crypto';

export const AUTH_COOKIE = 'tkmind_h5_session';

export function parseCookies(header = '') {
  return Object.fromEntries(
    header
      .split(';')
      .map((part) => part.trim())
      .filter(Boolean)
      .map((part) => {
        const separator = part.indexOf('=');
        if (separator < 0) return [part, ''];
        return [
          decodeURIComponent(part.slice(0, separator)),
          decodeURIComponent(part.slice(separator + 1)),
        ];
      }),
  );
}

function safeEqual(left, right) {
  const leftBuffer = Buffer.from(left);
  const rightBuffer = Buffer.from(right);
  return (
    leftBuffer.length === rightBuffer.length &&
    crypto.timingSafeEqual(leftBuffer, rightBuffer)
  );
}

export function createAuthManager({
  password,
  ttlMs = 7 * 24 * 60 * 60 * 1000,
  maxFailures = 5,
  failureWindowMs = 5 * 60 * 1000,
}) {
  if (!password) {
    throw new Error('H5_ACCESS_PASSWORD is required');
  }

  const sessions = new Map();
  const failures = new Map();

  const prune = (now = Date.now()) => {
    for (const [token, expiresAt] of sessions) {
      if (expiresAt <= now) sessions.delete(token);
    }
    for (const [ip, state] of failures) {
      if (state.resetAt <= now) failures.delete(ip);
    }
  };

  const login = (candidate, ip = 'unknown', now = Date.now()) => {
    prune(now);
    const failure = failures.get(ip);
    if (failure && failure.count >= maxFailures && failure.resetAt > now) {
      return { ok: false, retryAfterMs: failure.resetAt - now };
    }

    if (!safeEqual(candidate, password)) {
      const current =
        failure && failure.resetAt > now
          ? failure
          : { count: 0, resetAt: now + failureWindowMs };
      current.count += 1;
      failures.set(ip, current);
      return { ok: false, retryAfterMs: 0 };
    }

    failures.delete(ip);
    const token = crypto.randomBytes(32).toString('base64url');
    sessions.set(token, now + ttlMs);
    return { ok: true, token };
  };

  const verify = (token, now = Date.now()) => {
    if (!token) return false;
    prune(now);
    const expiresAt = sessions.get(token);
    if (!expiresAt || expiresAt <= now) {
      sessions.delete(token);
      return false;
    }
    sessions.set(token, now + ttlMs);
    return true;
  };

  const revoke = (token) => {
    if (token) sessions.delete(token);
  };

  return { login, verify, revoke };
}

export function sessionCookie(token, secure) {
  const parts = [
    `${AUTH_COOKIE}=${encodeURIComponent(token)}`,
    'Path=/',
    'HttpOnly',
    'SameSite=Lax',
    'Max-Age=604800',
  ];
  if (secure) parts.push('Secure');
  return parts.join('; ');
}

export function clearSessionCookie(secure) {
  const parts = [
    `${AUTH_COOKIE}=`,
    'Path=/',
    'HttpOnly',
    'SameSite=Lax',
    'Max-Age=0',
  ];
  if (secure) parts.push('Secure');
  return parts.join('; ');
}
