import fs from "node:fs";

export type AppConfig = {
  appEnv: string;
  appUrl?: string;
  host: string;
  port: number;
  ads: {
    enabled: boolean;
  };
  payments?: {
    stripeSecretKey?: string;
    stripeWebhookSecret?: string;
    stripeSuccessUrl?: string;
    stripeCancelUrl?: string;
  };
  auth: {
    jwtSecret: string;
  };
  cookies: {
    sameSite: "lax" | "strict" | "none";
    secure: boolean;
    domain?: string;
  };
  smtp: {
    host?: string;
    port?: number;
    secure: boolean;
    user?: string;
    password?: string;
    fromEmail?: string;
    fromName?: string;
  };
  db: {
    host: string;
    port: number;
    name: string;
    user: string;
    password: string;
    poolSize: number;
    ssl: boolean;
    sslRejectUnauthorized: boolean;
    sslCa?: string;
  };
};

const requireEnv = (key: string): string => {
  const value = process.env[key];
  if (!value) {
    throw new Error(`Missing env var: ${key}`);
  }
  return value;
};

const toNumber = (value: string, key: string): number => {
  const parsed = Number(value);
  if (!Number.isFinite(parsed) || parsed <= 0) {
    throw new Error(`Invalid number for ${key}`);
  }
  return parsed;
};

const toBool = (value?: string): boolean => {
  if (!value) {
    return false;
  }
  return value === "true" || value === "1";
};

const toSameSite = (value?: string): "lax" | "strict" | "none" => {
  const raw = value?.trim().toLowerCase();
  if (raw === "none" || raw === "strict" || raw === "lax") {
    return raw;
  }
  return "lax";
};

const readSecretFile = (filePath?: string): string | undefined => {
  if (!filePath) {
    return undefined;
  }
  if (!fs.existsSync(filePath)) {
    return undefined;
  }
  return fs.readFileSync(filePath, "utf8").trim();
};

const resolveSecret = (valueKey: string, fileKey: string): string => {
  const fromFile = readSecretFile(process.env[fileKey]);
  if (fromFile && fromFile.length > 0) {
    return fromFile;
  }
  const direct = process.env[valueKey];
  if (direct && direct.trim().length > 0) {
    return direct.trim();
  }
  throw new Error(`Missing secret for ${valueKey} or ${fileKey}`);
};

const resolveOptionalSecret = (
  valueKey: string,
  fileKey: string,
): string | undefined => {
  const fromFile = readSecretFile(process.env[fileKey]);
  if (fromFile && fromFile.length > 0) {
    return fromFile;
  }
  const direct = process.env[valueKey];
  if (direct && direct.trim().length > 0) {
    return direct.trim();
  }
  return undefined;
};

export const loadConfig = (): AppConfig => {
  const appEnv = process.env.APP_ENV ?? "development";
  const appUrl = process.env.APP_URL;
  const host = process.env.HOST ?? "0.0.0.0";
  const port = toNumber(process.env.PORT ?? "3000", "PORT");
  const adsEnabled = toBool(
    process.env.ADS_ENABLED ?? (appEnv === "production" ? "false" : "true"),
  );
  const jwtSecret = resolveSecret("JWT_SECRET", "JWT_SECRET_FILE");
  if (!jwtSecret) {
    throw new Error("Missing env var: JWT_SECRET or JWT_SECRET_FILE");
  }
  const dbHost = requireEnv("DB_HOST");
  const dbPort = toNumber(process.env.DB_PORT ?? "3306", "DB_PORT");
  const dbName = requireEnv("DB_NAME");
  const dbUser = requireEnv("DB_USER");
  const dbPassword = resolveSecret("DB_PASSWORD", "DB_PASSWORD_FILE");
  const poolSize = toNumber(process.env.DB_POOL_SIZE ?? "10", "DB_POOL_SIZE");
  const ssl = toBool(process.env.DB_SSL);
  const sslRejectUnauthorized = toBool(
    process.env.DB_SSL_REJECT_UNAUTHORIZED ?? "true",
  );
  const sslCa = readSecretFile(process.env.DB_SSL_CA_FILE);
  const smtpHost = process.env.SMTP_HOST?.trim();
  const smtpPortRaw = process.env.SMTP_PORT?.trim();
  const smtpPort = smtpPortRaw ? Number(smtpPortRaw) : undefined;
  const smtpSecure = toBool(process.env.SMTP_SECURE);
  const smtpUser = process.env.SMTP_USER?.trim();
  const smtpPassword = resolveOptionalSecret(
    "SMTP_PASSWORD",
    "SMTP_PASSWORD_FILE",
  );
  const smtpFromEmail = process.env.SMTP_FROM_EMAIL?.trim();
  const smtpFromName = process.env.SMTP_FROM_NAME?.trim();
  const cookieSameSite = toSameSite(process.env.COOKIE_SAMESITE);
  const cookieSecure =
    process.env.COOKIE_SECURE !== undefined
      ? toBool(process.env.COOKIE_SECURE)
      : appEnv === "production";
  const cookieDomain = process.env.COOKIE_DOMAIN?.trim();
  const stripeSecretKey = resolveOptionalSecret(
    "STRIPE_SECRET_KEY",
    "STRIPE_SECRET_KEY_FILE",
  );
  const stripeWebhookSecret = resolveOptionalSecret(
    "STRIPE_WEBHOOK_SECRET",
    "STRIPE_WEBHOOK_SECRET_FILE",
  );
  const stripeSuccessUrl = process.env.STRIPE_SUCCESS_URL?.trim();
  const stripeCancelUrl = process.env.STRIPE_CANCEL_URL?.trim();

  return {
    appEnv,
    appUrl,
    host,
    port,
    ads: {
      enabled: adsEnabled,
    },
    payments: {
      stripeSecretKey,
      stripeWebhookSecret,
      stripeSuccessUrl,
      stripeCancelUrl,
    },
    auth: {
      jwtSecret,
    },
    cookies: {
      sameSite: cookieSameSite,
      secure: cookieSecure,
      domain: cookieDomain,
    },
    smtp: {
      host: smtpHost,
      port: smtpPort,
      secure: smtpSecure,
      user: smtpUser,
      password: smtpPassword,
      fromEmail: smtpFromEmail,
      fromName: smtpFromName,
    },
    db: {
      host: dbHost,
      port: dbPort,
      name: dbName,
      user: dbUser,
      password: dbPassword,
      poolSize,
      ssl,
      sslRejectUnauthorized,
      sslCa,
    },
  };
};
