import "server-only";

import { z } from "zod";

const optionalString = z
  .string()
  .trim()
  .optional()
  .transform((value) => value || undefined);

const httpUrl = z
  .url({ protocol: /^https?$/ })
  .transform((value) => value.replace(/\/+$/, ""));

const schema = z.object({
  DATABASE_URL: z.string().min(1, "DATABASE_URL is required"),
  /** Absolute URL this CMS is served from. Local uploads are linked from here. */
  CMS_PUBLIC_URL: httpUrl.default("http://localhost:3200"),
  /** The public website that renders the blog. Used for previews and cache revalidation. */
  SITE_URL: httpUrl.optional(),
  /** Shared with the website's /api/revalidate endpoint. */
  SITE_REVALIDATE_SECRET: optionalString,
  /** Signs short-lived preview links for unpublished articles. */
  PREVIEW_TOKEN_SECRET: z
    .string()
    .min(32, "PREVIEW_TOKEN_SECRET must be at least 32 characters"),
  /** Bearer token for the scheduled-publishing cron endpoint. */
  CRON_SECRET: optionalString,
  STORAGE_DRIVER: z.enum(["local", "s3"]).default("local"),
  STORAGE_LOCAL_DIR: z.string().default("storage/uploads"),
  S3_BUCKET: optionalString,
  S3_REGION: optionalString,
  S3_ENDPOINT: optionalString,
  S3_ACCESS_KEY_ID: optionalString,
  S3_SECRET_ACCESS_KEY: optionalString,
  S3_PUBLIC_URL: optionalString,
  MEDIA_MAX_UPLOAD_MB: z.coerce.number().positive().max(50).default(10),
});

export type Env = z.infer<typeof schema>;

let cached: Env | undefined;

/**
 * Validated server environment. Parsed on first use rather than at import,
 * so `next build` works without runtime secrets.
 */
export function env(): Env {
  if (cached) return cached;
  const result = schema.safeParse(process.env);
  if (!result.success) {
    const issues = result.error.issues
      .map((issue) => `  - ${issue.path.join(".")}: ${issue.message}`)
      .join("\n");
    throw new Error(`Invalid environment variables:\n${issues}`);
  }
  cached = result.data;
  return cached;
}
