import "server-only";
import { z } from "zod";

const nonPlaceholder = z
  .string()
  .trim()
  .min(1)
  .refine(
    (value) => !/mock|replace[_-]?me|example\.(invalid|com)/i.test(value),
    "Replace the placeholder value",
  );
function isAllowedSiteUrl(value: string) {
  const url = new URL(value);
  if (url.protocol === "https:") return true;
  return (
    url.protocol === "http:" &&
    (url.hostname === "localhost" || url.hostname === "127.0.0.1")
  );
}

const deliverySchema = z.object({
  SITE_URL: z
    .url()
    .refine(
      isAllowedSiteUrl,
      "Use an HTTPS origin, or http://localhost for local Gmail testing",
    ),
  SMTP_HOST: nonPlaceholder.default("smtp.gmail.com"),
  SMTP_PORT: z.coerce
    .number()
    .int()
    .refine(
      (value) => value === 465 || value === 587,
      "Use 465 for implicit TLS or 587 for STARTTLS",
    )
    .default(465),
  SMTP_USER: z.email().pipe(nonPlaceholder),
  // Google shows app passwords in four spaced groups; the spaces are not part of it.
  SMTP_PASSWORD: z
    .string()
    .transform((value) => value.replace(/\s+/g, ""))
    .pipe(
      nonPlaceholder.min(
        16,
        "Supply a Gmail app password, not the account password",
      ),
    ),
  SMTP_FROM_EMAIL: z.email().pipe(nonPlaceholder),
  CONTACT_NOTIFICATION_EMAIL: z.email().pipe(nonPlaceholder),
});
const emailSchema = deliverySchema.extend({
  CONTACT_MODE: z.literal("email"),
});
const liveSchema = deliverySchema.extend({
  CONTACT_MODE: z.literal("live"),
  FIREBASE_PROJECT_ID: nonPlaceholder,
  FIREBASE_REGION: z.literal("asia-southeast1"),
  FIREBASE_CLIENT_EMAIL: z.email().pipe(nonPlaceholder),
  FIREBASE_PRIVATE_KEY: nonPlaceholder.refine(
    (value) =>
      value.includes("-----BEGIN PRIVATE KEY-----") &&
      value.includes("-----END PRIVATE KEY-----"),
    "Supply a PEM private key",
  ),
  TURNSTILE_SITE_KEY: nonPlaceholder,
  TURNSTILE_SECRET_KEY: nonPlaceholder,
  RATE_LIMIT_HMAC_SECRET: nonPlaceholder.min(32),
});

const invalid = (error: z.ZodError) =>
  new Error(
    `Invalid server configuration: ${[...new Set(error.issues.map((issue) => issue.path.join(".")))].join(", ")}`,
  );
function siteOrigin(value: string) {
  const origin = new URL(value);
  if (
    origin.pathname !== "/" ||
    origin.search ||
    origin.hash ||
    origin.username ||
    origin.password
  )
    throw new Error(
      "SITE_URL must be an origin without credentials, path, query or fragment.",
    );
  return origin.origin;
}

let productionMockWarned = false;
export function parseEnvironment(input: Record<string, string | undefined>) {
  const mode =
    input.CONTACT_MODE ?? (input.NODE_ENV === "production" ? "live" : "mock");
  if (mode === "mock") {
    if (input.VERCEL_ENV === "production" && !productionMockWarned) {
      productionMockWarned = true;
      console.warn(
        "CONTACT_MODE=mock on the production deployment: enquiries are acknowledged but discarded without email until live providers are configured.",
      );
    }
    return {
      CONTACT_MODE: "mock" as const,
      SITE_URL: "http://localhost:3100",
      FIREBASE_PROJECT_ID: "maxcredit-mock",
      FIREBASE_REGION: "asia-southeast1" as const,
      SMTP_FROM_EMAIL: "no-reply@example.invalid",
      CONTACT_NOTIFICATION_EMAIL: "staff@example.invalid",
      // Kept so an older application-providers.ts that still reads Resend can typecheck.
      RESEND_API_KEY: "",
      RESEND_FROM_EMAIL: "",
    };
  }
  // Gmail rewrites the From header to the mailbox that authenticated, unless the
  // address is a verified "send mail as" alias on that account.
  const delivery = {
    ...input,
    CONTACT_MODE: mode,
    SMTP_FROM_EMAIL: input.SMTP_FROM_EMAIL || input.SMTP_USER,
  };
  if (mode === "email") {
    const result = emailSchema.safeParse(delivery);
    if (!result.success) throw invalid(result.error);
    return {
      ...result.data,
      SITE_URL: siteOrigin(result.data.SITE_URL),
      RESEND_API_KEY: "",
      RESEND_FROM_EMAIL: "",
    };
  }
  const result = liveSchema.safeParse(delivery);
  if (!result.success) throw invalid(result.error);
  return {
    ...result.data,
    SITE_URL: siteOrigin(result.data.SITE_URL),
    FIREBASE_PRIVATE_KEY: result.data.FIREBASE_PRIVATE_KEY.replaceAll(
      "\\n",
      "\n",
    ),
    RESEND_API_KEY: "",
    RESEND_FROM_EMAIL: "",
  };
}

export type ServerEnvironment = ReturnType<typeof parseEnvironment>;
export const getServerEnvironment = () => parseEnvironment(process.env);
