import { z } from 'zod'

const APP_CONFIG_SCHEMA = z.object({
  apiURL: z.string(),
  baseURL: z.string(),
  keycloakURL: z.string(),
  keycloakRealm: z.string(),
  keycloakClientId: z.string(),
  mapBoxAccessToken: z.string(),
  mapBoxDisableTransitions: z.boolean().optional().default(false),
  useTestAssets: z.boolean().optional().default(false),
  enableAnalytics: z.boolean().optional().default(false),
  analyticsScriptUrl: z.string().optional(),
  AUTH: z.boolean().optional().default(true),
  enableDevTools: z.boolean().optional().default(false),
  checkLoginIframe: z.boolean().optional().default(true),
})

/**
 * A list of boolean environment variables.
 * When using the Docker multi-stage build, these variables are replaced placeholders - which are strings.
 * As the parser expects booleans, we need to manually convert them to `false` during the build.
 */
const DOCKER_BUILD_PLACEHOLDERS_BOOLEAN = new Set(
  ['VITE_PUBLIC_USE_AUTH', 'VITE_PUBLIC_ENABLE_ANALYTICS'].map((key) => `PLACEHOLDER_${key}`),
)

export const APP_CONFIG = APP_CONFIG_SCHEMA.parse({
  apiURL: viteenv(import.meta.env.VITE_PUBLIC_API_URL),
  baseURL: viteenv(import.meta.env.VITE_PUBLIC_BASE_URL),
  keycloakURL: viteenv(import.meta.env.VITE_PUBLIC_KEYCLOAK_URL),
  keycloakRealm: viteenv(import.meta.env.VITE_PUBLIC_KEYCLOAK_REALM),
  keycloakClientId: viteenv(import.meta.env.VITE_PUBLIC_KEYCLOAK_CLIENT_ID),
  mapBoxAccessToken: viteenv(import.meta.env.VITE_PUBLIC_MAPBOX_ACCESS_TOKEN),
  mapBoxDisableTransitions: viteenv(import.meta.env.VITE_PUBLIC_MAPBOX_DISABLE_TRANSITIONS),
  useTestAssets: viteenv(import.meta.env.VITE_PUBLIC_USE_TEST_ASSETS),
  enableAnalytics: viteenv(import.meta.env.VITE_PUBLIC_ENABLE_ANALYTICS),
  analyticsScriptUrl: viteenv(import.meta.env.VITE_PUBLIC_ANALYTICS_SCRIPT_URL),
  AUTH: viteenv(import.meta.env.VITE_AUTH),
  enableDevTools: viteenv(import.meta.env.VITE_ENABLE_DEV_TOOLS),
  checkLoginIframe: viteenv(import.meta.env.VITE_PUBLIC_CHECK_LOGIN_IFRAME),
})

/**
 * Transforms a stringified environment variable into its original type.
 *
 * @example
 *
 * ```ts
 * const value = viteenv(process.env.VITE_PUBLIC_ENV)
 * ```
 */

export function viteenv(value?: string | null): string | boolean | number | null | undefined {
  if (value == null) {
    return value
  }

  // During Next build, the env vars are replaced with placeholder strings.
  // This works fine for strings - but not for booleans. So we manually evaluate the placeholders to `false`
  // during the build. At first startup, the placeholders are replaced with the actual NEXT_PUBLIC_... reference -
  // which will be populated by Next at startup with the actual env variables.
  // See https://dev.to/itsrennyman/manage-nextpublic-environment-variables-at-runtime-with-docker-53dl
  if (DOCKER_BUILD_PLACEHOLDERS_BOOLEAN.has(value)) {
    return false
  }

  // NOTE: We need to parse the value as JSON because Next statically replaces all references to `process.env` with the
  // stringified values of the env vars at build time.
  // https://github.com/vercel/next.js/blob/9db91ebfa04d2ebf58f58ebd1ec2c26b46b8341a/packages/next/src/build/webpack-config.ts#L231
  try {
    return JSON.parse(value) as string | boolean | number | null | undefined
  } catch {
    // If we can't parse the value as JSON, just return the original value as string.
    return value
  }
}
