const DEVICE_COOKIE_NAME = "crm_device";
const DEVICE_COOKIE_MAX_AGE_SECONDS = 180 * 24 * 60 * 60;

export async function requireDeviceAuthentication(req, store) {
  const credential = deviceCredential(req);
  if (!credential) throw authenticationRequired();
  try {
    return await store.authenticate(credential);
  } catch (error) {
    if (["authentication_invalid", "authentication_revoked", "authentication_expired"].includes(error?.message)) {
      throw authenticationRequired();
    }
    throw error;
  }
}

export async function resolveRequestDevice(req, store, required = true) {
  if (required) return requireDeviceAuthentication(req, store);
  return {
    id: "authentication-exempt",
    label: "Authentication exempt",
    createdAt: null,
    expiresAt: null,
    revoked: false,
    current: true
  };
}

export async function resolvePublicDeviceAuthRoute(req, url, context) {
  if (req.method !== "POST" || url.pathname !== "/auth/pair") return null;
  const input = await context.readJson(req);
  try {
    const result = await context.store.completePairing({
      attemptId: input?.attemptId,
      code: input?.code,
      qrSecret: input?.qrSecret,
      label: input?.label
    });
    setDeviceCookie(context.res, req, result.credential);
    return { status: 201, body: { device: result.device } };
  } catch (error) {
    if (error?.message === "pairing_locked") throw publicAuthError("pairing_rate_limited", 429);
    if (["pairing_unavailable", "pairing_expired", "pairing_invalid"].includes(error?.message)) {
      throw publicAuthError("pairing_unavailable", 401);
    }
    throw error;
  }
}

export async function resolveDeviceAuthRoute(req, url, context) {
  if (req.method === "GET" && url.pathname === "/auth/session") {
    return { status: 200, body: { device: context.device } };
  }

  if (req.method === "GET" && url.pathname === "/auth/devices") {
    return { status: 200, body: { devices: await context.store.listDevices(context.device.id) } };
  }

  if (req.method === "POST" && url.pathname === "/auth/device/rotate") {
    const result = await context.store.rotateDevice(context.device.id);
    setDeviceCookie(context.res, req, result.credential);
    return { status: 200, body: { device: result.device } };
  }

  const revokeMatch = url.pathname.match(/^\/auth\/devices\/([^/]+)$/);
  if (req.method === "DELETE" && revokeMatch) {
    const deviceId = decodeURIComponent(revokeMatch[1]);
    const device = await context.store.revokeDevice(deviceId);
    if (deviceId === context.device.id) clearDeviceCookie(context.res, req);
    return { status: 200, body: { device } };
  }

  return null;
}

function deviceCredential(req) {
  const header = req.headers?.cookie;
  if (typeof header !== "string" || header.length > 8192) return "";
  const matches = header
    .split(";")
    .map((value) => value.trim())
    .filter((value) => value.startsWith(`${DEVICE_COOKIE_NAME}=`));
  if (matches.length !== 1) return "";
  return matches[0].slice(DEVICE_COOKIE_NAME.length + 1);
}

function setDeviceCookie(res, req, credential) {
  res.setHeader("set-cookie", serializeCookie(req, credential, DEVICE_COOKIE_MAX_AGE_SECONDS));
}

function clearDeviceCookie(res, req) {
  res.setHeader("set-cookie", serializeCookie(req, "", 0));
}

export function expireDeviceAuthentication(res, req) {
  clearDeviceCookie(res, req);
}

function serializeCookie(req, value, maxAge) {
  const origin = String(req.headers?.origin ?? "");
  const nativeCrossSite = origin === "https://localhost";
  const secure = origin.startsWith("https:");
  return [
    `${DEVICE_COOKIE_NAME}=${value}`,
    "Path=/api",
    "HttpOnly",
    `SameSite=${nativeCrossSite ? "None" : "Strict"}`,
    `Max-Age=${maxAge}`,
    ...(secure ? ["Secure"] : [])
  ].join("; ");
}

function authenticationRequired() {
  return publicAuthError("authentication_required", 401);
}

function publicAuthError(code, status) {
  const error = new Error(code);
  error.status = status;
  error.safeLogCode = code;
  return error;
}
