const SESSION_COOKIE_NAME = "auth";
const SESSION_TTL_SECONDS = 24 * 60 * 60;

type SessionPayload = {
  userId: string;
  tokenId: string;
  exp: number;
};

function toBase64Url(bytes: Uint8Array): string {
  let binary = "";
  for (const byte of bytes) {
    binary += String.fromCharCode(byte);
  }
  return btoa(binary)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/g, "");
}

function fromBase64Url(value: string): Uint8Array {
  const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
  const pad = base64.length % 4 === 0 ? "" : "=".repeat(4 - (base64.length % 4));
  const binary = atob(base64 + pad);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i += 1) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes;
}

function getSecretBytes(): Uint8Array | null {
  const secret = process.env.SESSION_SECRET;
  if (!secret) {
    return null;
  }
  return new TextEncoder().encode(secret);
}

async function importSigningKey(secretBytes: Uint8Array): Promise<CryptoKey> {
  const normalizedSecret = Uint8Array.from(secretBytes);
  return crypto.subtle.importKey(
    "raw",
    normalizedSecret,
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign", "verify"]
  );
}

async function sign(data: string): Promise<string> {
  const secretBytes = getSecretBytes();
  if (!secretBytes) {
    throw new Error("SESSION_SECRET is required");
  }
  const key = await importSigningKey(secretBytes);
  const payload = Uint8Array.from(new TextEncoder().encode(data));
  const signature = await crypto.subtle.sign("HMAC", key, payload);
  return toBase64Url(new Uint8Array(signature));
}

async function verify(data: string, signature: string): Promise<boolean> {
  const secretBytes = getSecretBytes();
  if (!secretBytes) {
    return false;
  }
  const key = await importSigningKey(secretBytes);
  const payload = Uint8Array.from(new TextEncoder().encode(data));
  const signatureBytes = Uint8Array.from(fromBase64Url(signature));
  return crypto.subtle.verify(
    "HMAC",
    key,
    signatureBytes,
    payload
  );
}

export function getSessionCookieName(): string {
  return SESSION_COOKIE_NAME;
}

export function getSessionTtlSeconds(days?: number): number {
  if (!days || Number.isNaN(days) || days <= 0) {
    return SESSION_TTL_SECONDS;
  }
  return Math.floor(days * 24 * 60 * 60);
}

export async function createSessionToken(days?: number): Promise<string> {
  const now = Math.floor(Date.now() / 1000);
  const ttl = getSessionTtlSeconds(days);
  const payload: SessionPayload = {
    userId: "default", // 单用户系统，使用固定 ID
    tokenId: crypto.randomUUID(),
    exp: now + ttl,
  };
  const encodedPayload = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));
  const signature = await sign(encodedPayload);
  return `${encodedPayload}.${signature}`;
}

export async function verifySessionToken(token: string): Promise<SessionPayload | null> {
  const [payloadPart, signaturePart] = token.split(".");
  if (!payloadPart || !signaturePart) {
    return null;
  }

  const isValidSignature = await verify(payloadPart, signaturePart);
  if (!isValidSignature) {
    return null;
  }

  try {
    const payloadText = new TextDecoder().decode(fromBase64Url(payloadPart));
    const payload = JSON.parse(payloadText) as SessionPayload;
    if (!payload.exp || typeof payload.exp !== "number") {
      return null;
    }
    if (!payload.userId || !payload.tokenId) {
      return null;
    }
    const now = Math.floor(Date.now() / 1000);
    if (payload.exp <= now) {
      return null;
    }
    return payload;
  } catch {
    return null;
  }
}
