import { siteUrl } from "@/lib/seo/site";

/**
 * Auth configuration derived from the environment.
 *
 * Both Google sign-in and outbound mail are optional: when their variables are
 * missing the corresponding feature reports itself as unavailable instead of
 * throwing, so a developer can run the app with nothing but a database. In
 * production the mailer refuses to fall back silently — see `mailer.ts`.
 */

export const SESSION_COOKIE = "sr_session";
export const OAUTH_STATE_COOKIE = "sr_oauth_state";
export const OAUTH_VERIFIER_COOKIE = "sr_oauth_verifier";
/**
 * Where the Google sign-in was started (e.g. `app.scan-receipts.app`), so the
 * callback can send the user back to the host they came from instead of always
 * dropping them on the main host. Short-lived, written only for hosts the
 * middleware routes as first-party (never an arbitrary URL — see
 * `src/lib/seo/site.ts`).
 */
export const OAUTH_RETURN_COOKIE = "sr_oauth_return";

/**
 * When set, every auth cookie (session, guest, CSRF, OAuth handshake) carries
 * this `Domain` attribute, so the session is shared across the main host and
 * the first-party `app.*` / `admin.*` subdomains that `src/middleware.ts`
 * routes to. Leave empty in local dev — localhost cookies must stay host-only.
 * Example for production: COOKIE_DOMAIN=.scan-receipts.app
 */
export const COOKIE_DOMAIN = (process.env.COOKIE_DOMAIN ?? "").trim();

/** How long a confirmation link stays valid. */
export const VERIFICATION_TTL_MS = 24 * 60 * 60 * 1000;

/**
 * Reset links live far shorter than confirmation links: they hand over control
 * of an existing account, so the window for a leaked inbox to be abused should
 * be small.
 */
export const PASSWORD_RESET_TTL_MS = 60 * 60 * 1000;

/** "Keep me signed in" versus a short working session. */
export const SESSION_TTL_REMEMBER_MS = 30 * 24 * 60 * 60 * 1000;
export const SESSION_TTL_DEFAULT_MS = 12 * 60 * 60 * 1000;

export const isProduction = process.env.NODE_ENV === "production";

/**
 * Header a native client sends to identify itself (`x-client: ios`). Its only
 * effect is to opt the response into returning the raw session token in the
 * JSON body (see login/signup) instead of relying on a cookie — browsers never
 * send this header, so the default (cookie-only, httpOnly, never in the body)
 * is unchanged for every existing web request.
 */
export const MOBILE_CLIENT_HEADER = "x-client";
const MOBILE_CLIENT_VALUES = new Set(["ios", "android"]);

export function isMobileClientRequest(request: {
  headers: { get(name: string): string | null };
}): boolean {
  const value = request.headers.get(MOBILE_CLIENT_HEADER);
  return value !== null && MOBILE_CLIENT_VALUES.has(value.toLowerCase());
}

export type SameSiteValue = "lax" | "strict" | "none";

/** Overridable knobs for `cookieSecurityOptions`. */
export interface CookieSecurityExtra {
  maxAge?: number;
  expires?: Date;
  sameSite?: SameSiteValue;
  /** Only the CSRF double-submit cookie needs this off — client JS must read it. */
  httpOnly?: boolean;
}

/**
 * Baseline attributes for every auth cookie, so a future write cannot quietly
 * skip a flag. Every writer (session, guest, OAuth handshake) goes through
 * this one builder:
 *
 * - `httpOnly: true` — never readable by client-side JavaScript.
 * - `sameSite: "lax"` — the CSRF-safe default; pass `"strict"` or `"none"`
 *   only as a deliberate override.
 * - `path: "/"` — visible app-wide, and the delete path has to line up with
 *   the write path.
 * - `secure: isProduction` — on under TLS, off in local dev. A Secure cookie
 *   is ignored on plain http://localhost, which would break the dev flow.
 *   Behind a TLS-terminating proxy the app must see the forwarded proto
 *   (X-Forwarded-Proto) so `isProduction` — and therefore `Secure` — stays
 *   accurate.
 *
 * `maxAge`, `expires` and `sameSite` overrides spread over the defaults.
 */
export function cookieSecurityOptions(extra: CookieSecurityExtra = {}) {
  const domain = COOKIE_DOMAIN
    ? COOKIE_DOMAIN.startsWith(".")
      ? COOKIE_DOMAIN
      : `.${COOKIE_DOMAIN}`
    : undefined;
  return {
    httpOnly: true,
    secure: isProduction,
    sameSite: "lax" as SameSiteValue,
    path: "/",
    // Domain-scoped cookies let one login serve the main host, app.* and
    // admin.* together. Left unset (host-only) in local development.
    ...(domain ? { domain } : {}),
    ...extra,
  };
}

/**
 * "Sign in with Apple" for the native iOS app (see `src/lib/auth/apple.ts`).
 * Unlike `googleOAuth`, there is no secret here — verification only needs to
 * check the identity token's `aud` claim matches the app that issued it.
 * For a NATIVE app (as opposed to a web "Sign in with Apple JS" client),
 * Apple sets `aud` to the app's bundle identifier itself, not a separately
 * registered Services ID — see project.yml's `PRODUCT_BUNDLE_IDENTIFIER`.
 */
export const appleSignIn = {
  bundleId: process.env.APPLE_APP_BUNDLE_ID || "app.scan-receipts.ios",
};

export const googleOAuth = {
  clientId: process.env.GOOGLE_CLIENT_ID ?? "",
  clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
  get redirectUri() {
    return `${siteUrl}/api/auth/google/callback`;
  },
  get isConfigured() {
    return Boolean(this.clientId && this.clientSecret);
  },
};

export const smtp = {
  host: process.env.SMTP_HOST ?? "",
  port: Number(process.env.SMTP_PORT ?? 587),
  user: process.env.SMTP_USER ?? "",
  password: process.env.SMTP_PASSWORD ?? "",
  /** Implicit TLS (port 465). Port 587 upgrades via STARTTLS instead. */
  secure: process.env.SMTP_SECURE === "true",
  from: process.env.MAIL_FROM || "ScanReceipts <no-reply@localhost>",
  get isConfigured() {
    return Boolean(this.host && this.user && this.password);
  },
};

export { siteUrl };
