export const SESSION_COOKIE_NAME = "__Host-awp_session";
export const INTERNAL_SESSION_HEADER = "x-awp-session";

export function sessionFromCookie(cookieHeader: string | undefined): string | undefined {
  if (!cookieHeader) return undefined;
  for (const part of cookieHeader.split(";")) {
    const [name, ...rest] = part.trim().split("=");
    if (name === SESSION_COOKIE_NAME) {
      const value = rest.join("=").trim();
      return value || undefined;
    }
  }
  return undefined;
}

export function setSessionCookie(token: string): string {
  return `${SESSION_COOKIE_NAME}=${token}; Path=/; HttpOnly; Secure; SameSite=Lax`;
}

export function clearSessionCookie(): string {
  return `${SESSION_COOKIE_NAME}=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
}

export function internalSessionHeaders(
  token: string,
  contentType?: string,
): Record<string, string> {
  return {
    [INTERNAL_SESSION_HEADER]: token,
    ...(contentType ? { "content-type": contentType } : {}),
  };
}

export function mutationIsCrossSite(input: {
  readonly method?: string;
  readonly origin?: string;
  readonly secFetchSite?: string;
  readonly host?: string;
}): boolean {
  const method = (input.method ?? "GET").toUpperCase();
  if (method === "GET" || method === "HEAD" || method === "OPTIONS") return false;
  if (input.secFetchSite?.toLowerCase() === "cross-site") return true;
  if (!input.origin) return false;
  try {
    return new URL(input.origin).host !== input.host;
  } catch {
    return true;
  }
}

export class LoginRateLimiter {
  private readonly attempts = new Map<string, { count: number; windowStartedAt: number }>();

  constructor(
    private readonly maxAttempts = 5,
    private readonly windowMs = 60_000,
    private readonly now: () => number = Date.now,
  ) {}

  allow(key: string): boolean {
    const now = this.now();
    const current = this.attempts.get(key);
    if (!current || now - current.windowStartedAt >= this.windowMs) {
      this.attempts.set(key, { count: 1, windowStartedAt: now });
      return true;
    }
    if (current.count >= this.maxAttempts) return false;
    current.count += 1;
    return true;
  }

  reset(key: string): void {
    this.attempts.delete(key);
  }
}

export function loginPage(error?: string): string {
  const message = error ? `<p role="alert">${escapeHtml(error)}</p>` : "";
  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="dark"><title>Sign in — AWP</title><style>body{font:15px system-ui;background:#0b1020;color:#e7eaf0;display:grid;place-items:center;min-height:100vh;margin:0}form{width:min(360px,calc(100vw - 48px));padding:24px;border:1px solid #2a3248;border-radius:12px;background:#11182b}h1{font-size:20px;margin:0 0 18px}label{display:block;margin-bottom:8px}input,button{box-sizing:border-box;width:100%;font:inherit;border-radius:8px;padding:10px 12px}input{color:inherit;background:#0b1020;border:1px solid #39435f}button{margin-top:14px;border:0;background:#7657ff;color:white;font-weight:600}p[role=alert]{color:#ff9b9b}</style></head><body><form method="post" action="/auth/login"><h1>AWP operator</h1>${message}<label for="password">Password</label><input id="password" name="password" type="password" autocomplete="current-password" required autofocus><button type="submit">Sign in</button></form></body></html>`;
}

function escapeHtml(value: string): string {
  return value.replace(
    /[&<>"']/g,
    (character) =>
      ({
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        '"': "&quot;",
        "'": "&#39;",
      })[character] ?? character,
  );
}
