export type EnvRecord = Readonly<Record<string, string | undefined>>;

export const DIMCODE_ENV_PREFIX = "DIMCODE_";

type RuntimeGlobals = typeof globalThis & {
  process?: Pick<NodeJS.Process, "env">;
};

export type EnvReader = Readonly<{
  env: EnvRecord;
  str: (key: string, ...aliases: string[]) => string;
  strAny: (keys: readonly string[]) => string;
  bool: (key: string, ...aliases: string[]) => boolean | undefined;
  boolAny: (keys: readonly string[]) => boolean | undefined;
  int: (key: string, ...aliases: string[]) => number | undefined;
  intAny: (keys: readonly string[]) => number | undefined;
  float: (key: string, ...aliases: string[]) => number | undefined;
  floatAny: (keys: readonly string[]) => number | undefined;
  prefixedStr: (name: string, ...aliases: string[]) => string;
  prefixedBool: (name: string, ...aliases: string[]) => boolean | undefined;
  prefixedInt: (name: string, ...aliases: string[]) => number | undefined;
  prefixedFloat: (name: string, ...aliases: string[]) => number | undefined;
}>;

function parseBool(value: string): boolean | undefined {
  const s = value.toLowerCase();
  if (!s) return undefined;
  if (s === "1" || s === "true" || s === "yes" || s === "on") return true;
  if (s === "0" || s === "false" || s === "no" || s === "off") return false;
  return undefined;
}

function parseIntValue(value: string): number | undefined {
  if (!value) return undefined;
  const n = Number.parseInt(value, 10);
  return Number.isFinite(n) ? n : undefined;
}

function parseFloatValue(value: string): number | undefined {
  if (!value) return undefined;
  const n = Number.parseFloat(value);
  return Number.isFinite(n) ? n : undefined;
}

function runtimeGlobals(): RuntimeGlobals {
  return globalThis as RuntimeGlobals;
}

export function readProcessEnv(): EnvRecord {
  return runtimeGlobals().process?.env ?? {};
}

export function resolveEnv(primary?: EnvRecord, fallback?: EnvRecord): EnvRecord {
  return { ...fallback, ...primary };
}

export function prefixedEnvKeys(name: string): readonly [string] {
  return [`${DIMCODE_ENV_PREFIX}${name}`] as const;
}

export function createEnvReader(env?: EnvRecord, fallback?: EnvRecord): EnvReader {
  const merged = resolveEnv(env, fallback);

  const strAny = (keys: readonly string[]): string => {
    for (const key of keys) {
      const raw = String(merged?.[key] ?? "").trim();
      if (raw) return raw;
    }
    return "";
  };

  const str = (key: string, ...aliases: string[]): string => {
    const raw = String(merged?.[key] ?? "").trim();
    if (raw) return raw;
    if (aliases.length) return strAny(aliases);
    return "";
  };

  const boolAny = (keys: readonly string[]): boolean | undefined => {
    const raw = strAny(keys);
    return parseBool(raw);
  };

  const bool = (key: string, ...aliases: string[]): boolean | undefined => {
    const raw = str(key, ...aliases);
    return parseBool(raw);
  };

  const intAny = (keys: readonly string[]): number | undefined => {
    const raw = strAny(keys);
    return parseIntValue(raw);
  };

  const int = (key: string, ...aliases: string[]): number | undefined => {
    const raw = str(key, ...aliases);
    return parseIntValue(raw);
  };

  const floatAny = (keys: readonly string[]): number | undefined => {
    const raw = strAny(keys);
    return parseFloatValue(raw);
  };

  const float = (key: string, ...aliases: string[]): number | undefined => {
    const raw = str(key, ...aliases);
    return parseFloatValue(raw);
  };

  const prefixedStr = (name: string, ...aliases: string[]): string => {
    return strAny([...prefixedEnvKeys(name), ...aliases]);
  };

  const prefixedBool = (name: string, ...aliases: string[]): boolean | undefined => {
    return boolAny([...prefixedEnvKeys(name), ...aliases]);
  };

  const prefixedInt = (name: string, ...aliases: string[]): number | undefined => {
    return intAny([...prefixedEnvKeys(name), ...aliases]);
  };

  const prefixedFloat = (name: string, ...aliases: string[]): number | undefined => {
    return floatAny([...prefixedEnvKeys(name), ...aliases]);
  };

  return {
    env: merged,
    str,
    strAny,
    bool,
    boolAny,
    int,
    intAny,
    float,
    floatAny,
    prefixedStr,
    prefixedBool,
    prefixedInt,
    prefixedFloat,
  } as const;
}

export function readPrefixedEnv(env: EnvRecord | undefined, name: string): string {
  const [primary] = prefixedEnvKeys(name);
  const source = env ?? readProcessEnv();
  return String(source?.[primary] ?? "").trim();
}

export function readPrefixedEnvFlag(env: EnvRecord | undefined, name: string): string {
  return readPrefixedEnv(env, name).toLowerCase();
}

export function setPrefixedEnvValue(
  env: Record<string, string | undefined>,
  name: string,
  value: string | undefined,
): void {
  const [primary] = prefixedEnvKeys(name);
  const raw = String(value ?? "").trim();
  if (!raw) {
    delete env[primary];
    return;
  }
  env[primary] = raw;
}

export function ensurePrefixedEnvValue(
  env: Record<string, string | undefined>,
  name: string,
  fallback: string,
): string {
  const [primary] = prefixedEnvKeys(name);
  const value = String(env[primary] ?? fallback ?? "").trim();
  if (!env[primary] && value) env[primary] = value;
  return value;
}
