import { vi } from "vitest";
import {
  afterAll,
  afterEach,
  beforeEach,
  describe,
  expect,
  test,
} from "@/test";
import config, {
  betaFeatureEnabled,
  getAnalyticsConfig,
  getCorsOrigins,
  getDatabaseUrl,
  getMCPGatewayOauthAllowedPublicHosts,
  getOtelExporterOtlpEndpoint,
  getOtelExporterOtlpLogEndpoint,
  getOtlpAuthHeaders,
  getTrustedOrigins,
  parseActiveChatRunPollIntervalMs,
  parseAnthropicWifConfig,
  parseAuditLogRetentionDays,
  parseBodyLimit,
  parseChatMaxOutputTokens,
  parseCodeRuntimeDaggerRunnerHost,
  parseCommaSeparatedList,
  parseConnectorSyncMaxDuration,
  parseContentMaxLength,
  parseDatabasePoolMax,
  parseDatabaseStatementTimeoutMillis,
  parseFileStorageFilesystemRoot,
  parseFileStorageProvider,
  parseFileStorageS3Config,
  parseLogFormat,
  parseMetricsPort,
  parseProcessType,
  parseSampleRate,
  parseTrustProxy,
  parseVirtualKeyDefaultExpiration,
} from "./config";

// Mock the logger
vi.mock("./logging", () => ({
  __esModule: true,
  default: {
    warn: vi.fn(),
    info: vi.fn(),
    error: vi.fn(),
  },
}));

import logger from "./logging";

describe("getAnalyticsConfig", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    process.env = { ...originalEnv };
    delete process.env.ARCHESTRA_ANALYTICS;
    delete process.env.ARCHESTRA_ANALYTICS_POSTHOG_KEY;
    delete process.env.ARCHESTRA_ANALYTICS_POSTHOG_HOST;
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  test("uses the default PostHog analytics config, enabled in production", () => {
    process.env.NODE_ENV = "production";

    expect(getAnalyticsConfig()).toEqual({
      enabled: true,
      posthog: {
        key: expect.stringMatching(/^phc_/),
        host: "https://eu.i.posthog.com",
      },
    });
  });

  test("defaults to disabled outside production", () => {
    process.env.NODE_ENV = "development";

    expect(getAnalyticsConfig().enabled).toBe(false);
  });

  test("explicit ARCHESTRA_ANALYTICS=enabled wins outside production", () => {
    process.env.NODE_ENV = "development";
    process.env.ARCHESTRA_ANALYTICS = "enabled";

    expect(getAnalyticsConfig().enabled).toBe(true);
  });

  test("explicit ARCHESTRA_ANALYTICS=disabled wins in production", () => {
    process.env.NODE_ENV = "production";
    process.env.ARCHESTRA_ANALYTICS = "disabled";

    expect(getAnalyticsConfig().enabled).toBe(false);
  });

  test("uses custom PostHog analytics env vars", () => {
    process.env.ARCHESTRA_ANALYTICS = "disabled";
    process.env.ARCHESTRA_ANALYTICS_POSTHOG_KEY = " ph_custom ";
    process.env.ARCHESTRA_ANALYTICS_POSTHOG_HOST =
      " https://posthog.example.com ";

    expect(getAnalyticsConfig()).toEqual({
      enabled: false,
      posthog: {
        key: "ph_custom",
        host: "https://posthog.example.com",
      },
    });
  });
});

describe("getDatabaseUrl", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    // Create a fresh copy of process.env for each test
    process.env = { ...originalEnv };
  });

  afterEach(() => {
    // Restore the original environment
    process.env = originalEnv;
  });

  test("should use ARCHESTRA_DATABASE_URL when both ARCHESTRA_DATABASE_URL and DATABASE_URL are set", () => {
    process.env.ARCHESTRA_DATABASE_URL =
      "postgresql://archestra:pass@host:5432/archestra_db";
    process.env.DATABASE_URL = "postgresql://other:pass@host:5432/other_db";

    const result = getDatabaseUrl();

    expect(result).toBe("postgresql://archestra:pass@host:5432/archestra_db");
  });

  test("should use DATABASE_URL when only DATABASE_URL is set", () => {
    delete process.env.ARCHESTRA_DATABASE_URL;
    process.env.DATABASE_URL = "postgresql://other:pass@host:5432/other_db";

    const result = getDatabaseUrl();

    expect(result).toBe("postgresql://other:pass@host:5432/other_db");
  });

  test("should use ARCHESTRA_DATABASE_URL when only ARCHESTRA_DATABASE_URL is set", () => {
    process.env.ARCHESTRA_DATABASE_URL =
      "postgresql://archestra:pass@host:5432/archestra_db";
    delete process.env.DATABASE_URL;

    const result = getDatabaseUrl();

    expect(result).toBe("postgresql://archestra:pass@host:5432/archestra_db");
  });

  test("should throw an error when neither ARCHESTRA_DATABASE_URL nor DATABASE_URL is set", () => {
    delete process.env.ARCHESTRA_DATABASE_URL;
    delete process.env.DATABASE_URL;

    expect(() => getDatabaseUrl()).toThrow(
      "Database URL is not set. Please set ARCHESTRA_DATABASE_URL or DATABASE_URL",
    );
  });

  test("should throw an error when both are empty strings", () => {
    process.env.ARCHESTRA_DATABASE_URL = "";
    process.env.DATABASE_URL = "";

    expect(() => getDatabaseUrl()).toThrow(
      "Database URL is not set. Please set ARCHESTRA_DATABASE_URL or DATABASE_URL",
    );
  });

  test("should use DATABASE_URL when ARCHESTRA_DATABASE_URL is empty string", () => {
    process.env.ARCHESTRA_DATABASE_URL = "";
    process.env.DATABASE_URL = "postgresql://other:pass@host:5432/other_db";

    const result = getDatabaseUrl();

    expect(result).toBe("postgresql://other:pass@host:5432/other_db");
  });
});

describe("getOtlpAuthHeaders", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    // Create a fresh copy of process.env for each test
    process.env = { ...originalEnv };
    // Clear mock calls
    vi.clearAllMocks();
  });

  afterEach(() => {
    // Restore the original environment
    process.env = originalEnv;
  });

  describe("Bearer token authentication", () => {
    test("should return Bearer authorization header when bearer token is provided", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_BEARER = "my-bearer-token";

      const result = getOtlpAuthHeaders();

      expect(result).toEqual({
        Authorization: "Bearer my-bearer-token",
      });
    });

    test("should prioritize bearer token over basic auth when both are provided", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_BEARER = "my-bearer-token";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME = "user";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD = "pass";

      const result = getOtlpAuthHeaders();

      expect(result).toEqual({
        Authorization: "Bearer my-bearer-token",
      });
    });

    test("should trim whitespace from bearer token", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_BEARER =
        "  my-bearer-token  ";

      const result = getOtlpAuthHeaders();

      expect(result).toEqual({
        Authorization: "Bearer my-bearer-token",
      });
    });
  });

  describe("Basic authentication", () => {
    test("should return Basic authorization header when both username and password are provided", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME = "testuser";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD = "testpass";

      const result = getOtlpAuthHeaders();

      // testuser:testpass in base64 is dGVzdHVzZXI6dGVzdHBhc3M=
      expect(result).toEqual({
        Authorization: "Basic dGVzdHVzZXI6dGVzdHBhc3M=",
      });
    });

    test("should trim whitespace from username and password", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME = "  testuser  ";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD = "  testpass  ";

      const result = getOtlpAuthHeaders();

      expect(result).toEqual({
        Authorization: "Basic dGVzdHVzZXI6dGVzdHBhc3M=",
      });
    });

    test("should return undefined and warn when only username is provided", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME = "testuser";
      delete process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD;

      const result = getOtlpAuthHeaders();

      expect(result).toBeUndefined();
      expect(logger.warn).toHaveBeenCalledWith(
        "OTEL authentication misconfigured: both ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME and ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD must be provided for basic auth",
      );
    });

    test("should return undefined and warn when only password is provided", () => {
      delete process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME;
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD = "testpass";

      const result = getOtlpAuthHeaders();

      expect(result).toBeUndefined();
      expect(logger.warn).toHaveBeenCalledWith(
        "OTEL authentication misconfigured: both ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME and ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD must be provided for basic auth",
      );
    });

    test("should return undefined and warn when username is empty string", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME = "";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD = "testpass";

      const result = getOtlpAuthHeaders();

      expect(result).toBeUndefined();
      expect(logger.warn).toHaveBeenCalledWith(
        "OTEL authentication misconfigured: both ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME and ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD must be provided for basic auth",
      );
    });

    test("should return undefined and warn when password is empty string", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME = "testuser";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD = "";

      const result = getOtlpAuthHeaders();

      expect(result).toBeUndefined();
      expect(logger.warn).toHaveBeenCalledWith(
        "OTEL authentication misconfigured: both ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME and ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD must be provided for basic auth",
      );
    });
  });

  describe("No authentication", () => {
    test("should return undefined when no authentication environment variables are set", () => {
      delete process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_BEARER;
      delete process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME;
      delete process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD;

      const result = getOtlpAuthHeaders();

      expect(result).toBeUndefined();
      expect(logger.warn).not.toHaveBeenCalled();
    });

    test("should return undefined when all authentication variables are empty strings", () => {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_BEARER = "";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_USERNAME = "";
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_AUTH_PASSWORD = "";

      const result = getOtlpAuthHeaders();

      expect(result).toBeUndefined();
      expect(logger.warn).not.toHaveBeenCalled();
    });
  });
});

describe("getConfiguredOrigins (tested via getCorsOrigins/getTrustedOrigins)", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    process.env = { ...originalEnv };
    // A local .env may set ARCHESTRA_NGROK_DOMAIN (a tunnel domain), which
    // getConfiguredOrigins folds into the trusted/CORS origins. Pin it empty so
    // these tests are independent of the developer's .env. Set to "" rather than
    // deleted: the re-import tests below reload config (and thus dotenv, which
    // defaults to override:false), so a deleted var would be repopulated from
    // .env while an already-set empty value is left untouched.
    process.env.ARCHESTRA_NGROK_DOMAIN = "";
    vi.clearAllMocks();
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  test("should accept all origins when no env vars are set", () => {
    delete process.env.ARCHESTRA_FRONTEND_URL;
    delete process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS;

    const cors = getCorsOrigins();
    expect(cors).toHaveLength(1);
    expect(cors[0]).toBeInstanceOf(RegExp);

    const trusted = getTrustedOrigins();
    expect(trusted).toEqual([
      "http://*:*",
      "https://*:*",
      "http://*",
      "https://*",
    ]);
  });

  test("should parse ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS with trimming and filtering", () => {
    process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS =
      "  http://keycloak:8080 , , https://auth.example.com  ";
    delete process.env.ARCHESTRA_FRONTEND_URL;

    const result = getTrustedOrigins();

    expect(result).toContain("http://keycloak:8080");
    expect(result).toContain("https://auth.example.com");
    expect(result).toHaveLength(2);
  });
});

describe("getTrustedOrigins", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    process.env = { ...originalEnv };
    // See note in getConfiguredOrigins: keep these origin tests independent of
    // a local .env that sets a tunnel domain.
    process.env.ARCHESTRA_NGROK_DOMAIN = "";
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  describe("no origin env vars (accept all)", () => {
    test("should return catch-all wildcards when no env vars are set", () => {
      delete process.env.ARCHESTRA_FRONTEND_URL;
      delete process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS;

      const result = getTrustedOrigins();

      expect(result).toEqual([
        "http://*:*",
        "https://*:*",
        "http://*",
        "https://*",
      ]);
    });
  });

  describe("configured origins (enforce)", () => {
    test("should return frontend URL when set", () => {
      process.env.ARCHESTRA_FRONTEND_URL = "https://app.example.com";
      delete process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS;

      expect(getTrustedOrigins()).toEqual(["https://app.example.com"]);
    });

    test("should combine frontend URL and additional origins", () => {
      process.env.ARCHESTRA_FRONTEND_URL = "https://app.example.com";
      process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS =
        "http://idp.example.com:8080";

      expect(getTrustedOrigins()).toEqual([
        "https://app.example.com",
        "http://idp.example.com:8080",
      ]);
    });

    test("should add 127.0.0.1 equivalent for localhost origins", () => {
      process.env.ARCHESTRA_FRONTEND_URL = "http://localhost:3000";
      delete process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS;

      const result = getTrustedOrigins();
      expect(result).toContain("http://localhost:3000");
      expect(result).toContain("http://127.0.0.1:3000");
    });

    test("should add localhost equivalent for 127.0.0.1 origins", () => {
      process.env.ARCHESTRA_FRONTEND_URL = "http://127.0.0.1:3000";
      delete process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS;

      const result = getTrustedOrigins();
      expect(result).toContain("http://127.0.0.1:3000");
      expect(result).toContain("http://localhost:3000");
    });

    test("should enforce only additional origins when frontend URL is not set", () => {
      delete process.env.ARCHESTRA_FRONTEND_URL;
      process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS =
        "https://auth.example.com";

      expect(getTrustedOrigins()).toEqual(["https://auth.example.com"]);
    });
  });
});

describe("parseBodyLimit", () => {
  const DEFAULT_VALUE = 1024; // 1KB default for testing

  describe("undefined or empty input", () => {
    test("should return default value when input is undefined", () => {
      expect(parseBodyLimit(undefined, DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });

    test("should return default value when input is empty string", () => {
      expect(parseBodyLimit("", DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });
  });

  describe("numeric bytes input", () => {
    test("should parse plain numeric value as bytes", () => {
      expect(parseBodyLimit("52428800", DEFAULT_VALUE)).toBe(52428800);
    });

    test("should parse small numeric value", () => {
      expect(parseBodyLimit("1024", DEFAULT_VALUE)).toBe(1024);
    });

    test("should parse zero", () => {
      expect(parseBodyLimit("0", DEFAULT_VALUE)).toBe(0);
    });
  });

  describe("human-readable format (KB)", () => {
    test("should parse KB lowercase", () => {
      expect(parseBodyLimit("100kb", DEFAULT_VALUE)).toBe(100 * 1024);
    });

    test("should parse KB uppercase", () => {
      expect(parseBodyLimit("100KB", DEFAULT_VALUE)).toBe(100 * 1024);
    });

    test("should parse KB mixed case", () => {
      expect(parseBodyLimit("100Kb", DEFAULT_VALUE)).toBe(100 * 1024);
    });
  });

  describe("human-readable format (MB)", () => {
    test("should parse MB lowercase", () => {
      expect(parseBodyLimit("50mb", DEFAULT_VALUE)).toBe(50 * 1024 * 1024);
    });

    test("should parse MB uppercase", () => {
      expect(parseBodyLimit("50MB", DEFAULT_VALUE)).toBe(50 * 1024 * 1024);
    });

    test("should parse MB mixed case", () => {
      expect(parseBodyLimit("50Mb", DEFAULT_VALUE)).toBe(50 * 1024 * 1024);
    });

    test("should parse 100MB correctly", () => {
      expect(parseBodyLimit("100MB", DEFAULT_VALUE)).toBe(100 * 1024 * 1024);
    });
  });

  describe("human-readable format (GB)", () => {
    test("should parse GB lowercase", () => {
      expect(parseBodyLimit("1gb", DEFAULT_VALUE)).toBe(1 * 1024 * 1024 * 1024);
    });

    test("should parse GB uppercase", () => {
      expect(parseBodyLimit("1GB", DEFAULT_VALUE)).toBe(1 * 1024 * 1024 * 1024);
    });

    test("should parse GB mixed case", () => {
      expect(parseBodyLimit("2Gb", DEFAULT_VALUE)).toBe(2 * 1024 * 1024 * 1024);
    });
  });

  describe("whitespace handling", () => {
    test("should handle leading whitespace", () => {
      expect(parseBodyLimit("  50MB", DEFAULT_VALUE)).toBe(50 * 1024 * 1024);
    });

    test("should handle trailing whitespace", () => {
      expect(parseBodyLimit("50MB  ", DEFAULT_VALUE)).toBe(50 * 1024 * 1024);
    });

    test("should handle surrounding whitespace", () => {
      expect(parseBodyLimit("  50MB  ", DEFAULT_VALUE)).toBe(50 * 1024 * 1024);
    });
  });

  describe("invalid input", () => {
    test("should return default value for invalid unit", () => {
      expect(parseBodyLimit("50TB", DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });

    test("should return default value for text without numbers", () => {
      expect(parseBodyLimit("MB", DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });

    test("should return default value for random text", () => {
      expect(parseBodyLimit("invalid", DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });

    test("should return default value for negative with unit", () => {
      expect(parseBodyLimit("-50MB", DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });

    test("should return default value for decimal with unit", () => {
      expect(parseBodyLimit("1.5MB", DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });

    test("should return default value for space between number and unit", () => {
      expect(parseBodyLimit("50 MB", DEFAULT_VALUE)).toBe(DEFAULT_VALUE);
    });
  });
});

describe("getOtelExporterOtlpEndpoint", () => {
  describe("default value", () => {
    test("should return default endpoint when no value provided", () => {
      const result = getOtelExporterOtlpEndpoint(undefined);
      expect(result).toBe("http://localhost:4318/v1/traces");
    });

    test("should return default endpoint when empty string provided", () => {
      const result = getOtelExporterOtlpEndpoint("");
      expect(result).toBe("http://localhost:4318/v1/traces");
    });

    test("should return default endpoint when only whitespace provided", () => {
      const result = getOtelExporterOtlpEndpoint("   ");
      expect(result).toBe("http://localhost:4318/v1/traces");
    });
  });

  describe("URL already ends with /v1/traces", () => {
    test("should return URL as-is when it ends with /v1/traces", () => {
      const result = getOtelExporterOtlpEndpoint(
        "http://otel-collector:4318/v1/traces",
      );
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });

    test("should normalize trailing slashes and return URL with /v1/traces", () => {
      const result = getOtelExporterOtlpEndpoint(
        "http://otel-collector:4318/v1/traces/",
      );
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });

    test("should handle multiple trailing slashes", () => {
      const result = getOtelExporterOtlpEndpoint(
        "http://otel-collector:4318/v1/traces///",
      );
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });
  });

  describe("URL ends with /v1", () => {
    test("should append /traces when URL ends with /v1", () => {
      const result = getOtelExporterOtlpEndpoint(
        "http://otel-collector:4318/v1",
      );
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });

    test("should handle /v1 with trailing slash", () => {
      const result = getOtelExporterOtlpEndpoint(
        "http://otel-collector:4318/v1/",
      );
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });
  });

  describe("URL without /v1/traces suffix", () => {
    test("should append /v1/traces to base URL", () => {
      const result = getOtelExporterOtlpEndpoint("http://otel-collector:4318");
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });

    test("should append /v1/traces to URL with trailing slash", () => {
      const result = getOtelExporterOtlpEndpoint("http://otel-collector:4318/");
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });

    test("should append /v1/traces to URL with custom path", () => {
      const result = getOtelExporterOtlpEndpoint(
        "http://otel-collector:4318/custom",
      );
      expect(result).toBe("http://otel-collector:4318/custom/v1/traces");
    });

    test("should handle $(NODE_IP) variable expansion syntax", () => {
      const result = getOtelExporterOtlpEndpoint("http://$(NODE_IP):4317");
      expect(result).toBe("http://$(NODE_IP):4317/v1/traces");
    });

    test("should preserve $(NODE_IP) and append /v1/traces", () => {
      const result = getOtelExporterOtlpEndpoint(
        "http://$(NODE_IP):4317/custom/path",
      );
      expect(result).toBe("http://$(NODE_IP):4317/custom/path/v1/traces");
    });
  });

  describe("HTTPS URLs", () => {
    test("should work with HTTPS URLs", () => {
      const result = getOtelExporterOtlpEndpoint("https://otel.example.com");
      expect(result).toBe("https://otel.example.com/v1/traces");
    });

    test("should work with HTTPS URLs that already have /v1/traces", () => {
      const result = getOtelExporterOtlpEndpoint(
        "https://otel.example.com/v1/traces",
      );
      expect(result).toBe("https://otel.example.com/v1/traces");
    });
  });

  describe("edge cases", () => {
    test("should handle URL with port but no path", () => {
      const result = getOtelExporterOtlpEndpoint("http://localhost:4317");
      expect(result).toBe("http://localhost:4317/v1/traces");
    });

    test("should handle URL without port", () => {
      const result = getOtelExporterOtlpEndpoint("http://otel-collector");
      expect(result).toBe("http://otel-collector/v1/traces");
    });

    test("should fix common typo /v1/trace (missing s) to /v1/traces", () => {
      // URL ending in /v1/trace (missing s) should be normalized to /v1/traces
      const result = getOtelExporterOtlpEndpoint(
        "http://otel-collector:4318/v1/trace",
      );
      expect(result).toBe("http://otel-collector:4318/v1/traces");
    });
  });
});

describe("getOtelExporterOtlpLogEndpoint", () => {
  const savedEnv = process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_ENDPOINT;

  afterAll(() => {
    if (savedEnv !== undefined) {
      process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_ENDPOINT = savedEnv;
    } else {
      delete process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_ENDPOINT;
    }
  });

  describe("default value", () => {
    test("should return default endpoint when no value provided", () => {
      delete process.env.ARCHESTRA_OTEL_EXPORTER_OTLP_ENDPOINT;
      const result = getOtelExporterOtlpLogEndpoint(undefined);
      expect(result).toBe("http://localhost:4318/v1/logs");
    });

    test("should return default endpoint when empty string provided", () => {
      const result = getOtelExporterOtlpLogEndpoint("");
      expect(result).toBe("http://localhost:4318/v1/logs");
    });

    test("should return default endpoint when only whitespace provided", () => {
      const result = getOtelExporterOtlpLogEndpoint("   ");
      expect(result).toBe("http://localhost:4318/v1/logs");
    });
  });

  describe("URL already ends with /v1/logs", () => {
    test("should return URL as-is when it ends with /v1/logs", () => {
      const result = getOtelExporterOtlpLogEndpoint(
        "http://otel-collector:4318/v1/logs",
      );
      expect(result).toBe("http://otel-collector:4318/v1/logs");
    });

    test("should normalize trailing slashes and return URL with /v1/logs", () => {
      const result = getOtelExporterOtlpLogEndpoint(
        "http://otel-collector:4318/v1/logs/",
      );
      expect(result).toBe("http://otel-collector:4318/v1/logs");
    });
  });

  describe("URL ends with /v1", () => {
    test("should append /logs when URL ends with /v1", () => {
      const result = getOtelExporterOtlpLogEndpoint(
        "http://otel-collector:4318/v1",
      );
      expect(result).toBe("http://otel-collector:4318/v1/logs");
    });

    test("should handle /v1 with trailing slash", () => {
      const result = getOtelExporterOtlpLogEndpoint(
        "http://otel-collector:4318/v1/",
      );
      expect(result).toBe("http://otel-collector:4318/v1/logs");
    });
  });

  describe("URL without /v1/logs suffix", () => {
    test("should append /v1/logs to base URL", () => {
      const result = getOtelExporterOtlpLogEndpoint(
        "http://otel-collector:4318",
      );
      expect(result).toBe("http://otel-collector:4318/v1/logs");
    });

    test("should append /v1/logs to URL with trailing slash", () => {
      const result = getOtelExporterOtlpLogEndpoint(
        "http://otel-collector:4318/",
      );
      expect(result).toBe("http://otel-collector:4318/v1/logs");
    });
  });

  describe("HTTPS URLs", () => {
    test("should work with HTTPS URLs", () => {
      const result = getOtelExporterOtlpLogEndpoint("https://otel.example.com");
      expect(result).toBe("https://otel.example.com/v1/logs");
    });

    test("should work with HTTPS URLs that already have /v1/logs", () => {
      const result = getOtelExporterOtlpLogEndpoint(
        "https://otel.example.com/v1/logs",
      );
      expect(result).toBe("https://otel.example.com/v1/logs");
    });
  });
});

describe("parseContentMaxLength", () => {
  test("should return default 10000 when no value provided", () => {
    expect(parseContentMaxLength(undefined)).toBe(10_000);
  });

  test("should return default when empty string provided", () => {
    expect(parseContentMaxLength("")).toBe(10_000);
  });

  test("should return default when whitespace-only string provided", () => {
    expect(parseContentMaxLength("   ")).toBe(10_000);
  });

  test("should parse valid integer value", () => {
    expect(parseContentMaxLength("5000")).toBe(5000);
  });

  test("should parse large value", () => {
    expect(parseContentMaxLength("100000")).toBe(100_000);
  });

  test("should trim whitespace and parse value", () => {
    expect(parseContentMaxLength("  8000  ")).toBe(8000);
  });

  test("should return default and warn for non-numeric value", () => {
    expect(parseContentMaxLength("abc")).toBe(10_000);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_OTEL_CONTENT_MAX_LENGTH value "abc", using default 10000',
    );
  });

  test("should return default and warn for zero", () => {
    expect(parseContentMaxLength("0")).toBe(10_000);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_OTEL_CONTENT_MAX_LENGTH value "0", using default 10000',
    );
  });

  test("should return default and warn for negative value", () => {
    expect(parseContentMaxLength("-100")).toBe(10_000);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_OTEL_CONTENT_MAX_LENGTH value "-100", using default 10000',
    );
  });
});

describe("parseChatMaxOutputTokens", () => {
  test("should return default 32768 when no value provided", () => {
    expect(parseChatMaxOutputTokens(undefined)).toBe(32768);
  });

  test("should return default when empty/whitespace string provided", () => {
    expect(parseChatMaxOutputTokens("")).toBe(32768);
    expect(parseChatMaxOutputTokens("   ")).toBe(32768);
  });

  test("should parse and trim a valid value", () => {
    expect(parseChatMaxOutputTokens("  16000  ")).toBe(16000);
  });

  test("should accept boundary values", () => {
    expect(parseChatMaxOutputTokens("1")).toBe(1);
    expect(parseChatMaxOutputTokens("1000000")).toBe(1000000);
  });

  test("should return default and warn for non-numeric value", () => {
    expect(parseChatMaxOutputTokens("abc")).toBe(32768);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_CHAT_MAX_OUTPUT_TOKENS value "abc", using default 32768',
    );
  });

  test("should reject fractional and trailing-garbage values instead of truncating", () => {
    expect(parseChatMaxOutputTokens("1.5")).toBe(32768);
    expect(parseChatMaxOutputTokens("32768abc")).toBe(32768);
    expect(parseChatMaxOutputTokens("Infinity")).toBe(32768);
  });

  test("should accept scientific notation for an integer value", () => {
    expect(parseChatMaxOutputTokens("1e6")).toBe(1000000);
  });

  test("should return default and warn for zero and out-of-range", () => {
    expect(parseChatMaxOutputTokens("0")).toBe(32768);
    expect(parseChatMaxOutputTokens("1000001")).toBe(32768);
  });
});

describe("parseDatabasePoolMax", () => {
  test("should return default 50 when no value provided", () => {
    expect(parseDatabasePoolMax(undefined)).toBe(50);
  });

  test("should return default when empty string provided", () => {
    expect(parseDatabasePoolMax("")).toBe(50);
  });

  test("should return default when whitespace-only string provided", () => {
    expect(parseDatabasePoolMax("   ")).toBe(50);
  });

  test("should parse valid value", () => {
    expect(parseDatabasePoolMax("100")).toBe(100);
  });

  test("should accept boundary values", () => {
    expect(parseDatabasePoolMax("1")).toBe(1);
    expect(parseDatabasePoolMax("500")).toBe(500);
  });

  test("should trim whitespace and parse value", () => {
    expect(parseDatabasePoolMax("  75  ")).toBe(75);
  });

  test("should return default and warn for non-numeric value", () => {
    expect(parseDatabasePoolMax("abc")).toBe(50);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_DATABASE_POOL_MAX value "abc", using default 50',
    );
  });

  test("should return default and warn for zero", () => {
    expect(parseDatabasePoolMax("0")).toBe(50);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_DATABASE_POOL_MAX value "0", using default 50',
    );
  });

  test("should return default and warn for negative value", () => {
    expect(parseDatabasePoolMax("-1")).toBe(50);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_DATABASE_POOL_MAX value "-1", using default 50',
    );
  });

  test("should return default and warn for value above cap", () => {
    expect(parseDatabasePoolMax("501")).toBe(50);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_DATABASE_POOL_MAX value "501", using default 50',
    );
  });
});

describe("parseDatabaseStatementTimeoutMillis", () => {
  test("should return default 30000 when no value provided", () => {
    expect(parseDatabaseStatementTimeoutMillis(undefined)).toBe(30000);
  });

  test("should return default when empty string provided", () => {
    expect(parseDatabaseStatementTimeoutMillis("")).toBe(30000);
  });

  test("should return default when whitespace-only string provided", () => {
    expect(parseDatabaseStatementTimeoutMillis("   ")).toBe(30000);
  });

  test("should parse valid value", () => {
    expect(parseDatabaseStatementTimeoutMillis("60000")).toBe(60000);
  });

  test("should trim whitespace and parse value", () => {
    expect(parseDatabaseStatementTimeoutMillis("  45000  ")).toBe(45000);
  });

  test("should allow 0 to disable the timeout", () => {
    expect(parseDatabaseStatementTimeoutMillis("0")).toBe(0);
  });

  test("should return default and warn for non-numeric value", () => {
    expect(parseDatabaseStatementTimeoutMillis("abc")).toBe(30000);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_DATABASE_STATEMENT_TIMEOUT_MILLIS value "abc", using default 30000',
    );
  });

  test("should return default and warn for negative value", () => {
    expect(parseDatabaseStatementTimeoutMillis("-1")).toBe(30000);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_DATABASE_STATEMENT_TIMEOUT_MILLIS value "-1", using default 30000',
    );
  });
});

describe("parseMetricsPort", () => {
  test("should return default 9050 when no value provided", () => {
    expect(parseMetricsPort(undefined)).toBe(9050);
  });

  test("should return default when empty string provided", () => {
    expect(parseMetricsPort("")).toBe(9050);
  });

  test("should return default when whitespace-only string provided", () => {
    expect(parseMetricsPort("   ")).toBe(9050);
  });

  test("should parse valid port value", () => {
    expect(parseMetricsPort("9051")).toBe(9051);
  });

  test("should accept boundary ports", () => {
    expect(parseMetricsPort("1")).toBe(1);
    expect(parseMetricsPort("65535")).toBe(65535);
  });

  test("should trim whitespace and parse value", () => {
    expect(parseMetricsPort("  9100  ")).toBe(9100);
  });

  test("should return default and warn for non-numeric value", () => {
    expect(parseMetricsPort("abc")).toBe(9050);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_METRICS_PORT value "abc", using default 9050',
    );
  });

  test("should return default and warn for zero", () => {
    expect(parseMetricsPort("0")).toBe(9050);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_METRICS_PORT value "0", using default 9050',
    );
  });

  test("should return default and warn for out-of-range port", () => {
    expect(parseMetricsPort("65536")).toBe(9050);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_METRICS_PORT value "65536", using default 9050',
    );
  });

  test("should return default and warn for negative value", () => {
    expect(parseMetricsPort("-1")).toBe(9050);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_METRICS_PORT value "-1", using default 9050',
    );
  });
});

describe("parseActiveChatRunPollIntervalMs", () => {
  test("returns default when value is missing", () => {
    expect(
      parseActiveChatRunPollIntervalMs({
        value: undefined,
        defaultValue: 500,
        envName: "ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS",
      }),
    ).toBe(500);
  });

  test("returns default when value is empty", () => {
    expect(
      parseActiveChatRunPollIntervalMs({
        value: "   ",
        defaultValue: 500,
        envName: "ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS",
      }),
    ).toBe(500);
  });

  test("parses a positive integer", () => {
    expect(
      parseActiveChatRunPollIntervalMs({
        value: "1000",
        defaultValue: 500,
        envName: "ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS",
      }),
    ).toBe(1000);
  });

  test("returns default and warns for zero", () => {
    expect(
      parseActiveChatRunPollIntervalMs({
        value: "0",
        defaultValue: 500,
        envName: "ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS",
      }),
    ).toBe(500);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS value "0", using default 500',
    );
  });

  test("returns default and warns for negative values", () => {
    expect(
      parseActiveChatRunPollIntervalMs({
        value: "-1",
        defaultValue: 500,
        envName: "ARCHESTRA_CHAT_ACTIVE_RUN_STOP_POLL_INTERVAL_MS",
      }),
    ).toBe(500);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_CHAT_ACTIVE_RUN_STOP_POLL_INTERVAL_MS value "-1", using default 500',
    );
  });

  test("returns default and warns for non-numeric values", () => {
    expect(
      parseActiveChatRunPollIntervalMs({
        value: "abc",
        defaultValue: 500,
        envName: "ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS",
      }),
    ).toBe(500);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS value "abc", using default 500',
    );
  });
});

describe("chat active run config", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    vi.resetModules();
    process.env = { ...originalEnv };
    process.env.ARCHESTRA_DATABASE_URL =
      "postgresql://archestra:pass@localhost:5432/archestra";
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  test("uses listen/notify by default", async () => {
    delete process.env.ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS;
    delete process.env.ARCHESTRA_CHAT_ACTIVE_RUN_STOP_POLL_INTERVAL_MS;
    delete process.env.ARCHESTRA_CHAT_ACTIVE_RUN_POLLING_COMPATIBILITY_ENABLED;
    delete process.env.ARCHESTRA_CHAT_ACTIVE_RUN_NOTIFY_DATABASE_URL;

    const { default: cfg } = await import("./config");

    expect(cfg.chat.activeRun).toMatchObject({
      replayPollIntervalMs: 500,
      stopPollIntervalMs: 30_000,
      pollingCompatibilityEnabled: false,
      notifyDatabaseUrl: "",
    });
  });

  test("reads active run polling compatibility env vars", async () => {
    process.env.ARCHESTRA_CHAT_ACTIVE_RUN_REPLAY_POLL_INTERVAL_MS = "750";
    process.env.ARCHESTRA_CHAT_ACTIVE_RUN_STOP_POLL_INTERVAL_MS = "1250";
    process.env.ARCHESTRA_CHAT_ACTIVE_RUN_POLLING_COMPATIBILITY_ENABLED =
      "true";
    process.env.ARCHESTRA_CHAT_ACTIVE_RUN_NOTIFY_DATABASE_URL =
      " postgresql://notify:pass@localhost:5432/archestra ";

    const { default: cfg } = await import("./config");

    expect(cfg.chat.activeRun).toMatchObject({
      replayPollIntervalMs: 750,
      stopPollIntervalMs: 1250,
      pollingCompatibilityEnabled: true,
      notifyDatabaseUrl: "postgresql://notify:pass@localhost:5432/archestra",
    });
  });

  test("keeps polling compatibility disabled for non-true values", async () => {
    process.env.ARCHESTRA_CHAT_ACTIVE_RUN_POLLING_COMPATIBILITY_ENABLED =
      "false";

    const { default: cfg } = await import("./config");

    expect(cfg.chat.activeRun.pollingCompatibilityEnabled).toBe(false);
  });

  test("uses short stop polling default in polling compatibility mode", async () => {
    delete process.env.ARCHESTRA_CHAT_ACTIVE_RUN_STOP_POLL_INTERVAL_MS;
    process.env.ARCHESTRA_CHAT_ACTIVE_RUN_POLLING_COMPATIBILITY_ENABLED =
      "true";

    const { default: cfg } = await import("./config");

    expect(cfg.chat.activeRun.stopPollIntervalMs).toBe(500);
  });
});

describe("getCorsOrigins", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    process.env = { ...originalEnv };
    // See note in getConfiguredOrigins: keep these origin tests independent of
    // a local .env that sets a tunnel domain.
    process.env.ARCHESTRA_NGROK_DOMAIN = "";
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  describe("no origin env vars (accept all)", () => {
    test("should return catch-all regex when no env vars are set", () => {
      delete process.env.ARCHESTRA_FRONTEND_URL;
      delete process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS;

      const result = getCorsOrigins();

      expect(result).toHaveLength(1);
      expect(result[0]).toBeInstanceOf(RegExp);
      expect((result[0] as RegExp).test("http://anything.example.com")).toBe(
        true,
      );
    });
  });

  describe("configured origins (enforce)", () => {
    beforeEach(() => {
      vi.resetModules();
    });

    test("should return frontend URL when set", async () => {
      process.env.NODE_ENV = "production";
      process.env.ARCHESTRA_FRONTEND_URL = "https://app.example.com";
      process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS = "";

      const { getCorsOrigins: fn } = await import("./config");
      expect(fn()).toEqual(["https://app.example.com"]);
    });

    test("should combine frontend URL and additional origins", async () => {
      process.env.NODE_ENV = "production";
      process.env.ARCHESTRA_FRONTEND_URL = "https://app.example.com";
      process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS =
        "http://idp.example.com:8080";

      const { getCorsOrigins: fn } = await import("./config");
      expect(fn()).toEqual([
        "https://app.example.com",
        "http://idp.example.com:8080",
      ]);
    });

    test("should add loopback equivalents for localhost origins", async () => {
      process.env.NODE_ENV = "production";
      process.env.ARCHESTRA_FRONTEND_URL = "http://localhost:3000";
      process.env.ARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS = "";

      const { getCorsOrigins: fn } = await import("./config");
      const result = fn();
      expect(result).toContain("http://localhost:3000");
      expect(result).toContain("http://127.0.0.1:3000");
    });
  });
});

describe("parseVirtualKeyDefaultExpiration", () => {
  test("should return default 2592000 when undefined", () => {
    expect(parseVirtualKeyDefaultExpiration(undefined)).toBe(2592000);
  });

  test("should return default when empty string", () => {
    expect(parseVirtualKeyDefaultExpiration("")).toBe(2592000);
  });

  test("should return default when whitespace-only", () => {
    expect(parseVirtualKeyDefaultExpiration("   ")).toBe(2592000);
  });

  test("should parse valid positive integer", () => {
    expect(parseVirtualKeyDefaultExpiration("86400")).toBe(86400);
  });

  test("should return 0 for zero (never expires)", () => {
    expect(parseVirtualKeyDefaultExpiration("0")).toBe(0);
  });

  test("should return default and warn for negative value", () => {
    expect(parseVirtualKeyDefaultExpiration("-100")).toBe(2592000);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_LLM_PROXY_VIRTUAL_KEYS_DEFAULT_EXPIRATION_SECONDS value "-100", using default 2592000',
    );
  });

  test("should return default and warn for non-numeric value", () => {
    expect(parseVirtualKeyDefaultExpiration("abc")).toBe(2592000);
    expect(logger.warn).toHaveBeenCalledWith(
      'Invalid ARCHESTRA_LLM_PROXY_VIRTUAL_KEYS_DEFAULT_EXPIRATION_SECONDS value "abc", using default 2592000',
    );
  });

  test("should trim whitespace and parse", () => {
    expect(parseVirtualKeyDefaultExpiration("  3600  ")).toBe(3600);
  });

  test("should cap values exceeding 1 year to 31536000", () => {
    expect(parseVirtualKeyDefaultExpiration("100000000")).toBe(31_536_000);
    expect(logger.warn).toHaveBeenCalledWith(
      'ARCHESTRA_LLM_PROXY_VIRTUAL_KEYS_DEFAULT_EXPIRATION_SECONDS value "100000000" exceeds maximum (31536000s / 1 year), capping to 31536000',
    );
  });

  test("should allow exactly 1 year (31536000)", () => {
    expect(parseVirtualKeyDefaultExpiration("31536000")).toBe(31_536_000);
  });

  test("should cap value just over 1 year", () => {
    expect(parseVirtualKeyDefaultExpiration("31536001")).toBe(31_536_000);
  });
});

describe("parseConnectorSyncMaxDuration", () => {
  test("should return default 3300 when undefined", () => {
    expect(parseConnectorSyncMaxDuration(undefined)).toBe(3300);
  });

  test("should return default 3300 when empty string", () => {
    expect(parseConnectorSyncMaxDuration("")).toBe(3300);
  });

  test("should parse valid positive integer", () => {
    expect(parseConnectorSyncMaxDuration("1800")).toBe(1800);
  });

  test("should return undefined for zero (disables time-bounded runs)", () => {
    expect(parseConnectorSyncMaxDuration("0")).toBeUndefined();
  });

  test("should return undefined for negative value", () => {
    expect(parseConnectorSyncMaxDuration("-100")).toBeUndefined();
  });

  test("should return undefined for non-numeric value", () => {
    expect(parseConnectorSyncMaxDuration("abc")).toBeUndefined();
  });

  test("should parse large value", () => {
    expect(parseConnectorSyncMaxDuration("7200")).toBe(7200);
  });
});

describe("parseFileStorageProvider", () => {
  test("defaults to db when unset", () => {
    expect(parseFileStorageProvider(undefined)).toBe("db");
  });

  test("returns filesystem (case/space-insensitive)", () => {
    expect(parseFileStorageProvider(" FileSystem ")).toBe("filesystem");
  });

  test("falls back to db for any unknown value", () => {
    expect(parseFileStorageProvider("nope")).toBe("db");
  });
});

describe("parseFileStorageProvider (s3)", () => {
  test("recognizes s3 (case-insensitive)", () => {
    expect(parseFileStorageProvider("s3")).toBe("s3");
    expect(parseFileStorageProvider("S3")).toBe("s3");
  });
  test("keeps filesystem and defaults unknown to db", () => {
    expect(parseFileStorageProvider("filesystem")).toBe("filesystem");
    expect(parseFileStorageProvider(undefined)).toBe("db");
    expect(parseFileStorageProvider("nope")).toBe("db");
  });
});

describe("parseFileStorageS3Config", () => {
  const env = {
    bucket: "my-bucket",
    region: "eu-west-1",
    endpoint: "https://minio.local:9000",
    forcePathStyle: "true",
    accessKeyId: "AKIA",
    secretAccessKey: "secret",
    keyPrefix: "/inst-a/",
  };
  test("parses a full s3 config", () => {
    const cfg = parseFileStorageS3Config({ provider: "s3", env });
    expect(cfg).toEqual({
      bucket: "my-bucket",
      region: "eu-west-1",
      endpoint: "https://minio.local:9000",
      forcePathStyle: true,
      accessKeyId: "AKIA",
      secretAccessKey: "secret",
      keyPrefix: "inst-a",
    });
  });
  test("defaults region, forcePathStyle, and keyPrefix", () => {
    const cfg = parseFileStorageS3Config({
      provider: "s3",
      env: {
        ...env,
        region: undefined,
        forcePathStyle: undefined,
        keyPrefix: undefined,
      },
    });
    expect(cfg.region).toBe("us-east-1");
    expect(cfg.forcePathStyle).toBe(false);
    expect(cfg.keyPrefix).toBe("");
  });
  test("throws when bucket is missing under the s3 provider", () => {
    expect(() =>
      parseFileStorageS3Config({
        provider: "s3",
        env: { ...env, bucket: undefined },
      }),
    ).toThrow(/ARCHESTRA_FILE_STORAGE_S3_BUCKET/);
  });
  test("does not validate when the provider is not s3", () => {
    expect(
      parseFileStorageS3Config({
        provider: "db",
        env: { ...env, bucket: undefined },
      }).bucket,
    ).toBe("");
  });
  test("throws when only one of the credential pair is set under s3", () => {
    expect(() =>
      parseFileStorageS3Config({
        provider: "s3",
        env: { ...env, secretAccessKey: undefined },
      }),
    ).toThrow(/must be set together/);
    expect(() =>
      parseFileStorageS3Config({
        provider: "s3",
        env: { ...env, accessKeyId: undefined },
      }),
    ).toThrow(/must be set together/);
  });
  test("treats a whitespace-only credential as unset under s3", () => {
    expect(() =>
      parseFileStorageS3Config({
        provider: "s3",
        env: { ...env, secretAccessKey: "   " },
      }),
    ).toThrow(/must be set together/);
  });
  test("allows both credentials omitted under s3 (AWS default chain)", () => {
    const cfg = parseFileStorageS3Config({
      provider: "s3",
      env: { ...env, accessKeyId: undefined, secretAccessKey: undefined },
    });
    expect(cfg.accessKeyId).toBeUndefined();
    expect(cfg.secretAccessKey).toBeUndefined();
  });
  test("does not reject a partial credential pair when the provider is not s3", () => {
    expect(
      parseFileStorageS3Config({
        provider: "db",
        env: { ...env, secretAccessKey: undefined },
      }).accessKeyId,
    ).toBe("AKIA");
  });
});

describe("parseFileStorageFilesystemRoot", () => {
  test("ignores the root when provider is db", () => {
    expect(parseFileStorageFilesystemRoot({ provider: "db", value: "" })).toBe(
      "",
    );
  });

  test("trims a configured absolute root for the filesystem provider", () => {
    expect(
      parseFileStorageFilesystemRoot({
        provider: "filesystem",
        value: "  /data/archestra_results  ",
      }),
    ).toBe("/data/archestra_results");
  });

  test("requires a root when provider is filesystem", () => {
    expect(() =>
      parseFileStorageFilesystemRoot({ provider: "filesystem", value: " " }),
    ).toThrow(
      "ARCHESTRA_FILE_STORAGE_FILESYSTEM_ROOT is required when ARCHESTRA_FILE_STORAGE_PROVIDER=filesystem",
    );
  });

  test("rejects a relative root for the filesystem provider", () => {
    expect(() =>
      parseFileStorageFilesystemRoot({
        provider: "filesystem",
        value: "relative/dir",
      }),
    ).toThrow(
      "ARCHESTRA_FILE_STORAGE_FILESYSTEM_ROOT must be an absolute path",
    );
  });
});
describe("parseProcessType", () => {
  test("should return 'all' when undefined", () => {
    expect(parseProcessType(undefined)).toBe("all");
  });

  test("should return 'all' when empty string", () => {
    expect(parseProcessType("")).toBe("all");
  });

  test("should return 'web' for 'web'", () => {
    expect(parseProcessType("web")).toBe("web");
  });

  test("should return 'worker' for 'worker'", () => {
    expect(parseProcessType("worker")).toBe("worker");
  });

  test("should be case insensitive", () => {
    expect(parseProcessType("WEB")).toBe("web");
    expect(parseProcessType("WORKER")).toBe("worker");
    expect(parseProcessType("Web")).toBe("web");
    expect(parseProcessType("Worker")).toBe("worker");
  });

  test("should return 'all' for unknown values", () => {
    expect(parseProcessType("unknown")).toBe("all");
    expect(parseProcessType("both")).toBe("all");
    expect(parseProcessType("api")).toBe("all");
  });

  test.each([
    { input: undefined, processType: "all", webServer: true, worker: true },
    { input: "", processType: "all", webServer: true, worker: true },
    { input: "all", processType: "all", webServer: true, worker: true },
    { input: "web", processType: "web", webServer: true, worker: false },
    { input: "WEB", processType: "web", webServer: true, worker: false },
    { input: "worker", processType: "worker", webServer: false, worker: true },
    { input: "WORKER", processType: "worker", webServer: false, worker: true },
    { input: "unknown", processType: "all", webServer: true, worker: true },
  ])("input=$input → shouldRunWebServer=$webServer, shouldRunWorker=$worker", ({
    input,
    processType,
    webServer,
    worker,
  }) => {
    const result = parseProcessType(input);
    expect(result).toBe(processType);
    // These match the derivation: shouldRunWebServer = processType !== "worker", shouldRunWorker = processType !== "web"
    expect(result !== "worker").toBe(webServer);
    expect(result !== "web").toBe(worker);
  });
});

describe("parseSampleRate", () => {
  test("should return default when undefined", () => {
    expect(parseSampleRate(undefined, 0.2)).toBe(0.2);
  });

  test("should return default when empty string", () => {
    expect(parseSampleRate("", 0.05)).toBe(0.05);
  });

  test("should parse valid rate", () => {
    expect(parseSampleRate("0.5", 0.2)).toBe(0.5);
  });

  test("should parse 0", () => {
    expect(parseSampleRate("0", 0.2)).toBe(0);
  });

  test("should parse 1", () => {
    expect(parseSampleRate("1", 0.2)).toBe(1);
  });

  test("should return default for value above 1", () => {
    expect(parseSampleRate("1.5", 0.2)).toBe(0.2);
  });

  test("should return default for negative value", () => {
    expect(parseSampleRate("-0.1", 0.3)).toBe(0.3);
  });

  test("should return default for non-numeric value", () => {
    expect(parseSampleRate("abc", 0.1)).toBe(0.1);
  });
});

describe("parseCodeRuntimeDaggerRunnerHost", () => {
  test("should return undefined when runtime is disabled and host is unset", () => {
    expect(
      parseCodeRuntimeDaggerRunnerHost({ enabled: false, envValue: undefined }),
    ).toBeUndefined();
  });

  test("should not validate host while runtime is disabled", () => {
    expect(
      parseCodeRuntimeDaggerRunnerHost({
        enabled: false,
        envValue: "kube-pod://dagger-engine?namespace=dagger",
      }),
    ).toBe("kube-pod://dagger-engine?namespace=dagger");
  });

  test("should return undefined when runtime is enabled but host is unset", () => {
    expect(
      parseCodeRuntimeDaggerRunnerHost({ enabled: true, envValue: undefined }),
    ).toBeUndefined();
  });

  test("should trim and return kube-pod runner host", () => {
    expect(
      parseCodeRuntimeDaggerRunnerHost({
        enabled: true,
        envValue:
          " kube-pod://dagger-runtime-engine-0?namespace=dagger&container=dagger-engine ",
      }),
    ).toBe(
      "kube-pod://dagger-runtime-engine-0?namespace=dagger&container=dagger-engine",
    );
  });

  test("should trim and return TCP runner host", () => {
    expect(
      parseCodeRuntimeDaggerRunnerHost({
        enabled: true,
        envValue: " tcp://dagger-runtime.dagger.svc.cluster.local:1234 ",
      }),
    ).toBe("tcp://dagger-runtime.dagger.svc.cluster.local:1234");
  });

  test("should return undefined for unsupported runner hosts", () => {
    expect(
      parseCodeRuntimeDaggerRunnerHost({
        enabled: true,
        envValue: "unix:///run/dagger/engine.sock",
      }),
    ).toBeUndefined();
  });
});

describe("parseCommaSeparatedList", () => {
  test("should parse comma-separated values", () => {
    expect(parseCommaSeparatedList("anthropic,amazon")).toEqual([
      "anthropic",
      "amazon",
    ]);
  });

  test("should trim whitespace from values", () => {
    expect(parseCommaSeparatedList(" anthropic , amazon ")).toEqual([
      "anthropic",
      "amazon",
    ]);
  });

  test("should return empty array for empty string", () => {
    expect(parseCommaSeparatedList("")).toEqual([]);
  });

  test("should filter out empty entries from extra commas", () => {
    expect(parseCommaSeparatedList("anthropic,,amazon,")).toEqual([
      "anthropic",
      "amazon",
    ]);
  });

  test("should handle single value", () => {
    expect(parseCommaSeparatedList("anthropic")).toEqual(["anthropic"]);
  });
});

describe("parseTrustProxy", () => {
  test("should return false when undefined", () => {
    expect(parseTrustProxy(undefined)).toBe(false);
  });

  test("should return false when empty string", () => {
    expect(parseTrustProxy("")).toBe(false);
  });

  test("should return false when whitespace-only", () => {
    expect(parseTrustProxy("   ")).toBe(false);
  });

  test('should return false for "false"', () => {
    expect(parseTrustProxy("false")).toBe(false);
  });

  test('should return true for "true"', () => {
    expect(parseTrustProxy("true")).toBe(true);
  });

  test("should trim whitespace and return true", () => {
    expect(parseTrustProxy("  true  ")).toBe(true);
  });

  test("should return string for a single IP", () => {
    expect(parseTrustProxy("127.0.0.1")).toBe("127.0.0.1");
  });

  test("should return string for a single CIDR", () => {
    expect(parseTrustProxy("192.168.1.0/24")).toBe("192.168.1.0/24");
  });

  test("should return normalised string for comma-separated IPs", () => {
    expect(parseTrustProxy("127.0.0.1,10.0.0.1")).toBe("127.0.0.1,10.0.0.1");
  });

  test("should return normalised string for comma-separated CIDRs", () => {
    expect(parseTrustProxy("10.0.0.0/8,172.16.0.0/12,192.168.0.0/16")).toBe(
      "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
    );
  });

  test("should trim whitespace around each IP in a comma-separated list", () => {
    expect(parseTrustProxy("  127.0.0.1 , 10.0.0.1  ")).toBe(
      "127.0.0.1,10.0.0.1",
    );
  });

  test("should filter empty entries from extra commas", () => {
    expect(parseTrustProxy("127.0.0.1,,10.0.0.1")).toBe("127.0.0.1,10.0.0.1");
  });
});

describe("getMCPGatewayOauthAllowedPublicHosts", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    process.env = { ...originalEnv };
    delete process.env.ARCHESTRA_API_BASE_URL;
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  // ARCHESTRA_FRONTEND_URL is captured at module load (config.frontendBaseUrl),
  // so it can't be mutated per-test. We assert the function pulls that captured
  // value through, and exercise the ARCHESTRA_API_BASE_URL path
  // (which is read fresh on every call) for the rest of the behavior.

  test("always includes the frontendBaseUrl host", () => {
    const hosts = getMCPGatewayOauthAllowedPublicHosts();
    expect(hosts.size).toBeGreaterThan(0);
    expect(hosts.has(new URL(config.frontendBaseUrl).host.toLowerCase())).toBe(
      true,
    );
  });

  test("includes the frontend host plus local dev origins when ARCHESTRA_API_BASE_URL is unset", () => {
    const expected = new URL(config.frontendBaseUrl).host.toLowerCase();
    const hosts = getMCPGatewayOauthAllowedPublicHosts();
    expect(hosts.has(expected)).toBe(true);
    // Local dev origins are always allow-listed in development so a configured
    // tunnel (ARCHESTRA_FRONTEND_URL) can't break localhost MCP connections.
    expect(hosts.has("localhost:3000")).toBe(true);
    expect(hosts.has("127.0.0.1:3000")).toBe(true);
  });

  test("includes a single ARCHESTRA_API_BASE_URL host", () => {
    process.env.ARCHESTRA_API_BASE_URL = "https://api.example.com";
    expect(getMCPGatewayOauthAllowedPublicHosts().has("api.example.com")).toBe(
      true,
    );
  });

  test("splits comma-separated ARCHESTRA_API_BASE_URL", () => {
    process.env.ARCHESTRA_API_BASE_URL =
      "https://api.example.com,https://internal.svc:9000";
    const hosts = getMCPGatewayOauthAllowedPublicHosts();
    expect(hosts.has("api.example.com")).toBe(true);
    expect(hosts.has("internal.svc:9000")).toBe(true);
  });

  test("strips default ports (80 for http, 443 for https)", () => {
    process.env.ARCHESTRA_API_BASE_URL =
      "https://api.example.com:443,http://other.example.com:80";
    const hosts = getMCPGatewayOauthAllowedPublicHosts();
    expect(hosts.has("api.example.com")).toBe(true);
    expect(hosts.has("other.example.com")).toBe(true);
  });

  test("keeps explicit non-default ports", () => {
    process.env.ARCHESTRA_API_BASE_URL =
      "http://something.example:9000,https://api.example.com:8443";
    const hosts = getMCPGatewayOauthAllowedPublicHosts();
    expect(hosts.has("something.example:9000")).toBe(true);
    expect(hosts.has("api.example.com:8443")).toBe(true);
  });

  test("lowercases hostnames", () => {
    process.env.ARCHESTRA_API_BASE_URL = "https://Api.Example.COM";
    expect(getMCPGatewayOauthAllowedPublicHosts().has("api.example.com")).toBe(
      true,
    );
  });

  test("trims whitespace around comma-separated URLs", () => {
    process.env.ARCHESTRA_API_BASE_URL =
      "  https://api.example.com , https://internal.svc:9000  ";
    const hosts = getMCPGatewayOauthAllowedPublicHosts();
    expect(hosts.has("api.example.com")).toBe(true);
    expect(hosts.has("internal.svc:9000")).toBe(true);
  });

  test("ignores empty entries from extra commas", () => {
    process.env.ARCHESTRA_API_BASE_URL =
      "https://api.example.com,,https://internal.svc:9000";
    const hosts = getMCPGatewayOauthAllowedPublicHosts();
    expect(hosts.has("api.example.com")).toBe(true);
    expect(hosts.has("internal.svc:9000")).toBe(true);
  });

  test("ignores malformed URLs without failing", () => {
    process.env.ARCHESTRA_API_BASE_URL = "not-a-url,https://api.example.com";
    expect(getMCPGatewayOauthAllowedPublicHosts().has("api.example.com")).toBe(
      true,
    );
  });
});

describe("parseAuditLogRetentionDays", () => {
  test("returns 0 (disabled) when env var is not set", () => {
    expect(parseAuditLogRetentionDays(undefined)).toBe(0);
  });

  test("returns 0 (disabled) when env var is empty string", () => {
    expect(parseAuditLogRetentionDays("")).toBe(0);
  });

  test("returns 0 to keep the sweep disabled", () => {
    expect(parseAuditLogRetentionDays("0")).toBe(0);
  });

  test("returns a valid positive integer (opt-in)", () => {
    expect(parseAuditLogRetentionDays("90")).toBe(90);
    expect(parseAuditLogRetentionDays("365")).toBe(365);
  });

  test("trims whitespace before parsing", () => {
    expect(parseAuditLogRetentionDays("  30  ")).toBe(30);
  });

  test("returns default and warns on non-numeric value", () => {
    expect(parseAuditLogRetentionDays("abc")).toBe(0);
    expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining("abc"));
  });

  test("returns default and warns on negative value", () => {
    expect(parseAuditLogRetentionDays("-1")).toBe(0);
    expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining("-1"));
  });
});

describe("betaFeatureEnabled", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    process.env = { ...originalEnv };
    delete process.env.ARCHESTRA_BETA;
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  describe("with ARCHESTRA_BETA unset", () => {
    test("an unset flag stays off", () => {
      expect(betaFeatureEnabled(undefined)).toBe(false);
    });

    test("a blank flag stays off", () => {
      expect(betaFeatureEnabled("")).toBe(false);
    });

    test('an explicit "true" enables the flag', () => {
      expect(betaFeatureEnabled("true")).toBe(true);
    });

    test('an explicit "false" disables the flag', () => {
      expect(betaFeatureEnabled("false")).toBe(false);
    });
  });

  describe("with ARCHESTRA_BETA=true", () => {
    beforeEach(() => {
      process.env.ARCHESTRA_BETA = "true";
    });

    test("an unset flag falls back to beta (on)", () => {
      expect(betaFeatureEnabled(undefined)).toBe(true);
    });

    test("a blank flag falls back to beta (on)", () => {
      expect(betaFeatureEnabled("")).toBe(true);
    });

    test('an explicit "false" still wins over beta', () => {
      expect(betaFeatureEnabled("false")).toBe(false);
    });

    test('an explicit "true" stays on', () => {
      expect(betaFeatureEnabled("true")).toBe(true);
    });
  });

  describe("with ARCHESTRA_BETA set to a non-true value", () => {
    test('"false" does not trigger the fallback', () => {
      process.env.ARCHESTRA_BETA = "false";
      expect(betaFeatureEnabled(undefined)).toBe(false);
    });

    test("any other value is treated as off", () => {
      process.env.ARCHESTRA_BETA = "1";
      expect(betaFeatureEnabled(undefined)).toBe(false);
    });
  });

  test('only the exact string "true" enables a flag', () => {
    expect(betaFeatureEnabled("TRUE")).toBe(false);
    expect(betaFeatureEnabled("yes")).toBe(false);
    expect(betaFeatureEnabled("1")).toBe(false);
  });
});

describe("parseLogFormat", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  test('accepts "pretty"', () => {
    expect(parseLogFormat("pretty")).toBe("pretty");
  });

  test('accepts "json"', () => {
    expect(parseLogFormat("json")).toBe("json");
  });

  test("is case-insensitive and trims whitespace", () => {
    expect(parseLogFormat("  PRETTY  ")).toBe("pretty");
    expect(parseLogFormat("Json")).toBe("json");
  });

  test('defaults to "json" when undefined or empty without warning', () => {
    expect(parseLogFormat(undefined)).toBe("json");
    expect(parseLogFormat("")).toBe("json");
    expect(parseLogFormat("   ")).toBe("json");
    expect(logger.warn).not.toHaveBeenCalled();
  });

  test('warns and falls back to "json" on unknown values', () => {
    expect(parseLogFormat("xml")).toBe("json");
    expect(logger.warn).toHaveBeenCalledWith(
      expect.stringContaining('Invalid ARCHESTRA_LOGGING_FORMAT value "xml"'),
    );
  });
});

describe("parseAnthropicWifConfig", () => {
  const completeEnv = {
    federationRuleId: "fdrl_test",
    organizationId: "00000000-0000-0000-0000-000000000000",
    serviceAccountId: "svac_test",
    identityTokenFile: "/var/run/secrets/anthropic.com/token",
  };

  test("returns null when nothing is set", () => {
    expect(parseAnthropicWifConfig({})).toBeNull();
  });

  test("parses a complete configuration with a token file", () => {
    expect(parseAnthropicWifConfig(completeEnv)).toEqual({
      federationRuleId: "fdrl_test",
      organizationId: "00000000-0000-0000-0000-000000000000",
      serviceAccountId: "svac_test",
      identityTokenFile: "/var/run/secrets/anthropic.com/token",
    });
  });

  test("accepts an inline identity token as the token source", () => {
    expect(
      parseAnthropicWifConfig({
        ...completeEnv,
        identityTokenFile: undefined,
        identityToken: "jwt-inline",
      }),
    ).toMatchObject({ identityToken: "jwt-inline" });
  });

  test("includes the optional workspace ID when set", () => {
    expect(
      parseAnthropicWifConfig({ ...completeEnv, workspaceId: "wrkspc_test" }),
    ).toMatchObject({ workspaceId: "wrkspc_test" });
  });

  test.each([
    ["federationRuleId", { ...completeEnv, federationRuleId: undefined }],
    ["organizationId", { ...completeEnv, organizationId: undefined }],
    ["serviceAccountId", { ...completeEnv, serviceAccountId: undefined }],
    ["token source", { ...completeEnv, identityTokenFile: undefined }],
  ])("disables WIF when %s is missing", (_label, env) => {
    expect(parseAnthropicWifConfig(env)).toBeNull();
  });

  test("treats whitespace-only values as unset", () => {
    expect(
      parseAnthropicWifConfig({ ...completeEnv, federationRuleId: "  " }),
    ).toBeNull();
  });
});
