import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import type { PrismaClient } from "@prisma/client";

// Mock better-auth modules to inspect genericOAuth plugin configuration
const mockGenericOAuth = vi.fn().mockReturnValue({ id: "generic-oauth" });
const mockBetterAuth = vi.fn().mockReturnValue({ handler: vi.fn() });
const mockPrismaAdapter = vi.fn().mockReturnValue({});

vi.mock("better-auth/plugins", () => ({
  genericOAuth: (...args: unknown[]) => mockGenericOAuth(...args),
}));

vi.mock("better-auth", () => ({
  betterAuth: (...args: unknown[]) => mockBetterAuth(...args),
}));

vi.mock("better-auth/adapters/prisma", () => ({
  prismaAdapter: (...args: unknown[]) => mockPrismaAdapter(...args),
}));

import {
  isOidcEnabled,
  validateOidcConfig,
  createAuth,
  getTrustedOrigins,
  getBetterAuthBaseUrl,
} from "./auth.config";

describe("auth.config", () => {
  // Store original env vars to restore after each test
  const originalEnv = { ...process.env };

  beforeEach(() => {
    // Clear relevant env vars before each test
    delete process.env.OIDC_ENABLED;
    delete process.env.OIDC_ISSUER;
    delete process.env.OIDC_CLIENT_ID;
    delete process.env.OIDC_CLIENT_SECRET;
    delete process.env.OIDC_REDIRECT_URI;
    delete process.env.NODE_ENV;
    delete process.env.BETTER_AUTH_URL;
    delete process.env.NEXT_PUBLIC_APP_URL;
    delete process.env.NEXT_PUBLIC_API_URL;
    delete process.env.TRUSTED_ORIGINS;
    delete process.env.COOKIE_DOMAIN;
  });

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

  describe("isOidcEnabled", () => {
    it("should return false when OIDC_ENABLED is not set", () => {
      expect(isOidcEnabled()).toBe(false);
    });

    it("should return false when OIDC_ENABLED is 'false'", () => {
      process.env.OIDC_ENABLED = "false";
      expect(isOidcEnabled()).toBe(false);
    });

    it("should return false when OIDC_ENABLED is '0'", () => {
      process.env.OIDC_ENABLED = "0";
      expect(isOidcEnabled()).toBe(false);
    });

    it("should return false when OIDC_ENABLED is empty string", () => {
      process.env.OIDC_ENABLED = "";
      expect(isOidcEnabled()).toBe(false);
    });

    it("should return true when OIDC_ENABLED is 'true'", () => {
      process.env.OIDC_ENABLED = "true";
      expect(isOidcEnabled()).toBe(true);
    });

    it("should return true when OIDC_ENABLED is '1'", () => {
      process.env.OIDC_ENABLED = "1";
      expect(isOidcEnabled()).toBe(true);
    });
  });

  describe("validateOidcConfig", () => {
    describe("when OIDC is disabled", () => {
      it("should not throw when OIDC_ENABLED is not set", () => {
        expect(() => validateOidcConfig()).not.toThrow();
      });

      it("should not throw when OIDC_ENABLED is false even if vars are missing", () => {
        process.env.OIDC_ENABLED = "false";
        // Intentionally not setting any OIDC vars
        expect(() => validateOidcConfig()).not.toThrow();
      });
    });

    describe("when OIDC is enabled", () => {
      beforeEach(() => {
        process.env.OIDC_ENABLED = "true";
      });

      it("should throw when OIDC_ISSUER is missing", () => {
        process.env.OIDC_CLIENT_ID = "test-client-id";
        process.env.OIDC_CLIENT_SECRET = "test-client-secret";
        process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

        expect(() => validateOidcConfig()).toThrow("OIDC_ISSUER");
        expect(() => validateOidcConfig()).toThrow("OIDC authentication is enabled");
      });

      it("should throw when OIDC_CLIENT_ID is missing", () => {
        process.env.OIDC_ISSUER = "https://auth.example.com/";
        process.env.OIDC_CLIENT_SECRET = "test-client-secret";
        process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

        expect(() => validateOidcConfig()).toThrow("OIDC_CLIENT_ID");
      });

      it("should throw when OIDC_CLIENT_SECRET is missing", () => {
        process.env.OIDC_ISSUER = "https://auth.example.com/";
        process.env.OIDC_CLIENT_ID = "test-client-id";
        process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

        expect(() => validateOidcConfig()).toThrow("OIDC_CLIENT_SECRET");
      });

      it("should throw when OIDC_REDIRECT_URI is missing", () => {
        process.env.OIDC_ISSUER = "https://auth.example.com/";
        process.env.OIDC_CLIENT_ID = "test-client-id";
        process.env.OIDC_CLIENT_SECRET = "test-client-secret";

        expect(() => validateOidcConfig()).toThrow("OIDC_REDIRECT_URI");
      });

      it("should throw when all required vars are missing", () => {
        expect(() => validateOidcConfig()).toThrow(
          "OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI"
        );
      });

      it("should throw when vars are empty strings", () => {
        process.env.OIDC_ISSUER = "";
        process.env.OIDC_CLIENT_ID = "";
        process.env.OIDC_CLIENT_SECRET = "";
        process.env.OIDC_REDIRECT_URI = "";

        expect(() => validateOidcConfig()).toThrow(
          "OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI"
        );
      });

      it("should throw when vars are whitespace only", () => {
        process.env.OIDC_ISSUER = "   ";
        process.env.OIDC_CLIENT_ID = "test-client-id";
        process.env.OIDC_CLIENT_SECRET = "test-client-secret";
        process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

        expect(() => validateOidcConfig()).toThrow("OIDC_ISSUER");
      });

      it("should throw when OIDC_ISSUER does not end with trailing slash", () => {
        process.env.OIDC_ISSUER = "https://auth.example.com/application/o/mosaic";
        process.env.OIDC_CLIENT_ID = "test-client-id";
        process.env.OIDC_CLIENT_SECRET = "test-client-secret";
        process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

        expect(() => validateOidcConfig()).toThrow("OIDC_ISSUER must end with a trailing slash");
        expect(() => validateOidcConfig()).toThrow("https://auth.example.com/application/o/mosaic");
      });

      it("should not throw with valid complete configuration", () => {
        process.env.OIDC_ISSUER = "https://auth.example.com/application/o/mosaic-stack/";
        process.env.OIDC_CLIENT_ID = "test-client-id";
        process.env.OIDC_CLIENT_SECRET = "test-client-secret";
        process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

        expect(() => validateOidcConfig()).not.toThrow();
      });

      it("should suggest disabling OIDC in error message", () => {
        expect(() => validateOidcConfig()).toThrow("OIDC_ENABLED=false");
      });

      describe("OIDC_REDIRECT_URI validation", () => {
        beforeEach(() => {
          process.env.OIDC_ISSUER = "https://auth.example.com/application/o/mosaic-stack/";
          process.env.OIDC_CLIENT_ID = "test-client-id";
          process.env.OIDC_CLIENT_SECRET = "test-client-secret";
        });

        it("should throw when OIDC_REDIRECT_URI is not a valid URL", () => {
          process.env.OIDC_REDIRECT_URI = "not-a-url";

          expect(() => validateOidcConfig()).toThrow("OIDC_REDIRECT_URI must be a valid URL");
          expect(() => validateOidcConfig()).toThrow("not-a-url");
          expect(() => validateOidcConfig()).toThrow("Parse error:");
        });

        it("should throw when OIDC_REDIRECT_URI path does not start with /auth/oauth2/callback", () => {
          process.env.OIDC_REDIRECT_URI = "https://app.example.com/oauth/callback";

          expect(() => validateOidcConfig()).toThrow(
            'OIDC_REDIRECT_URI path must start with "/auth/oauth2/callback"'
          );
          expect(() => validateOidcConfig()).toThrow("/oauth/callback");
        });

        it("should accept a valid OIDC_REDIRECT_URI with /auth/oauth2/callback path", () => {
          process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

          expect(() => validateOidcConfig()).not.toThrow();
        });

        it("should accept OIDC_REDIRECT_URI with exactly /auth/oauth2/callback path", () => {
          process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback";

          expect(() => validateOidcConfig()).not.toThrow();
        });

        it("should warn but not throw when using localhost in production", () => {
          process.env.NODE_ENV = "production";
          process.env.OIDC_REDIRECT_URI = "http://localhost:3000/auth/oauth2/callback/authentik";

          const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});

          expect(() => validateOidcConfig()).not.toThrow();
          expect(warnSpy).toHaveBeenCalledWith(
            expect.stringContaining("OIDC_REDIRECT_URI uses localhost")
          );

          warnSpy.mockRestore();
        });

        it("should warn but not throw when using 127.0.0.1 in production", () => {
          process.env.NODE_ENV = "production";
          process.env.OIDC_REDIRECT_URI = "http://127.0.0.1:3000/auth/oauth2/callback/authentik";

          const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});

          expect(() => validateOidcConfig()).not.toThrow();
          expect(warnSpy).toHaveBeenCalledWith(
            expect.stringContaining("OIDC_REDIRECT_URI uses localhost")
          );

          warnSpy.mockRestore();
        });

        it("should not warn about localhost when not in production", () => {
          process.env.NODE_ENV = "development";
          process.env.OIDC_REDIRECT_URI = "http://localhost:3000/auth/oauth2/callback/authentik";

          const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});

          expect(() => validateOidcConfig()).not.toThrow();
          expect(warnSpy).not.toHaveBeenCalled();

          warnSpy.mockRestore();
        });
      });
    });
  });

  describe("createAuth - genericOAuth PKCE configuration", () => {
    beforeEach(() => {
      mockGenericOAuth.mockClear();
      mockBetterAuth.mockClear();
      mockPrismaAdapter.mockClear();
    });

    it("should enable PKCE in the genericOAuth provider config when OIDC is enabled", () => {
      process.env.OIDC_ENABLED = "true";
      process.env.OIDC_ISSUER = "https://auth.example.com/application/o/mosaic-stack/";
      process.env.OIDC_CLIENT_ID = "test-client-id";
      process.env.OIDC_CLIENT_SECRET = "test-client-secret";
      process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";

      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockGenericOAuth).toHaveBeenCalledOnce();
      const callArgs = mockGenericOAuth.mock.calls[0][0] as {
        config: Array<{ pkce?: boolean; redirectURI?: string }>;
      };
      expect(callArgs.config[0].pkce).toBe(true);
      expect(callArgs.config[0].redirectURI).toBe(
        "https://app.example.com/auth/oauth2/callback/authentik"
      );
    });

    it("should not call genericOAuth when OIDC is disabled", () => {
      process.env.OIDC_ENABLED = "false";

      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockGenericOAuth).not.toHaveBeenCalled();
    });

    it("should throw if OIDC_CLIENT_ID is missing when OIDC is enabled", () => {
      process.env.OIDC_ENABLED = "true";
      process.env.OIDC_ISSUER = "https://auth.example.com/application/o/mosaic-stack/";
      process.env.OIDC_CLIENT_SECRET = "test-client-secret";
      process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";
      // OIDC_CLIENT_ID deliberately not set

      // validateOidcConfig will throw first, so we need to bypass it
      // by setting the var then deleting it after validation
      // Instead, test via the validation path which is fine — but let's
      // verify the plugin-level guard by using a direct approach:
      // Set env to pass validateOidcConfig, then delete OIDC_CLIENT_ID
      // The validateOidcConfig will catch this first, which is correct behavior
      const mockPrisma = {} as PrismaClient;
      expect(() => createAuth(mockPrisma)).toThrow("OIDC_CLIENT_ID");
    });

    it("should throw if OIDC_CLIENT_SECRET is missing when OIDC is enabled", () => {
      process.env.OIDC_ENABLED = "true";
      process.env.OIDC_ISSUER = "https://auth.example.com/application/o/mosaic-stack/";
      process.env.OIDC_CLIENT_ID = "test-client-id";
      process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";
      // OIDC_CLIENT_SECRET deliberately not set

      const mockPrisma = {} as PrismaClient;
      expect(() => createAuth(mockPrisma)).toThrow("OIDC_CLIENT_SECRET");
    });

    it("should throw if OIDC_ISSUER is missing when OIDC is enabled", () => {
      process.env.OIDC_ENABLED = "true";
      process.env.OIDC_CLIENT_ID = "test-client-id";
      process.env.OIDC_CLIENT_SECRET = "test-client-secret";
      process.env.OIDC_REDIRECT_URI = "https://app.example.com/auth/oauth2/callback/authentik";
      // OIDC_ISSUER deliberately not set

      const mockPrisma = {} as PrismaClient;
      expect(() => createAuth(mockPrisma)).toThrow("OIDC_ISSUER");
    });
  });

  describe("getTrustedOrigins", () => {
    it("should return localhost URLs when NODE_ENV is not production", () => {
      process.env.NODE_ENV = "development";

      const origins = getTrustedOrigins();

      expect(origins).toContain("http://localhost:3000");
      expect(origins).toContain("http://localhost:3001");
    });

    it("should return localhost URLs when NODE_ENV is not set", () => {
      // NODE_ENV is deleted in beforeEach, so it's undefined here
      const origins = getTrustedOrigins();

      expect(origins).toContain("http://localhost:3000");
      expect(origins).toContain("http://localhost:3001");
    });

    it("should exclude localhost URLs in production", () => {
      process.env.NODE_ENV = "production";

      const origins = getTrustedOrigins();

      expect(origins).not.toContain("http://localhost:3000");
      expect(origins).not.toContain("http://localhost:3001");
    });

    it("should parse TRUSTED_ORIGINS comma-separated values", () => {
      process.env.TRUSTED_ORIGINS = "https://app.mosaicstack.dev,https://api.mosaicstack.dev";

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://app.mosaicstack.dev");
      expect(origins).toContain("https://api.mosaicstack.dev");
    });

    it("should trim whitespace from TRUSTED_ORIGINS entries", () => {
      process.env.TRUSTED_ORIGINS = " https://app.mosaicstack.dev , https://api.mosaicstack.dev ";

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://app.mosaicstack.dev");
      expect(origins).toContain("https://api.mosaicstack.dev");
    });

    it("should filter out empty strings from TRUSTED_ORIGINS", () => {
      process.env.TRUSTED_ORIGINS = "https://app.mosaicstack.dev,,, ,";

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://app.mosaicstack.dev");
      // No empty strings in the result
      origins.forEach((o) => expect(o).not.toBe(""));
    });

    it("should include NEXT_PUBLIC_APP_URL", () => {
      process.env.NEXT_PUBLIC_APP_URL = "https://my-app.example.com";

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://my-app.example.com");
    });

    it("should include NEXT_PUBLIC_API_URL", () => {
      process.env.NEXT_PUBLIC_API_URL = "https://my-api.example.com";

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://my-api.example.com");
    });

    it("should deduplicate origins", () => {
      process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000";
      process.env.TRUSTED_ORIGINS = "http://localhost:3000,http://localhost:3001";
      // NODE_ENV not set, so localhost fallbacks are also added

      const origins = getTrustedOrigins();

      const countLocalhost3000 = origins.filter((o) => o === "http://localhost:3000").length;
      const countLocalhost3001 = origins.filter((o) => o === "http://localhost:3001").length;
      expect(countLocalhost3000).toBe(1);
      expect(countLocalhost3001).toBe(1);
    });

    it("should handle all env vars missing gracefully", () => {
      // All env vars deleted in beforeEach; NODE_ENV is also deleted (not production)
      const origins = getTrustedOrigins();

      // Should still return localhost fallbacks since not in production
      expect(origins).toContain("http://localhost:3000");
      expect(origins).toContain("http://localhost:3001");
      expect(origins).toHaveLength(2);
    });

    it("should return empty array when all env vars missing in production", () => {
      process.env.NODE_ENV = "production";

      const origins = getTrustedOrigins();

      expect(origins).toHaveLength(0);
    });

    it("should combine all sources correctly", () => {
      process.env.NEXT_PUBLIC_APP_URL = "https://app.mosaicstack.dev";
      process.env.NEXT_PUBLIC_API_URL = "https://api.mosaicstack.dev";
      process.env.TRUSTED_ORIGINS = "https://extra.example.com";
      process.env.NODE_ENV = "development";

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://app.mosaicstack.dev");
      expect(origins).toContain("https://api.mosaicstack.dev");
      expect(origins).toContain("https://extra.example.com");
      expect(origins).toContain("http://localhost:3000");
      expect(origins).toContain("http://localhost:3001");
      expect(origins).toHaveLength(5);
    });

    it("should reject invalid URLs in TRUSTED_ORIGINS with a warning including error details", () => {
      process.env.TRUSTED_ORIGINS = "not-a-url,https://valid.example.com";
      process.env.NODE_ENV = "production";

      const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://valid.example.com");
      expect(origins).not.toContain("not-a-url");
      expect(warnSpy).toHaveBeenCalledWith(
        expect.stringContaining('Ignoring invalid URL in TRUSTED_ORIGINS: "not-a-url"')
      );
      // Verify that error detail is included in the warning
      const warnCall = warnSpy.mock.calls.find(
        (call) => typeof call[0] === "string" && call[0].includes("not-a-url")
      );
      expect(warnCall).toBeDefined();
      expect(warnCall![0]).toMatch(/\(.*\)$/);

      warnSpy.mockRestore();
    });

    it("should reject non-HTTP origins in TRUSTED_ORIGINS with a warning", () => {
      process.env.TRUSTED_ORIGINS = "ftp://files.example.com,https://valid.example.com";
      process.env.NODE_ENV = "production";

      const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});

      const origins = getTrustedOrigins();

      expect(origins).toContain("https://valid.example.com");
      expect(origins).not.toContain("ftp://files.example.com");
      expect(warnSpy).toHaveBeenCalledWith(
        expect.stringContaining("Ignoring non-HTTP origin in TRUSTED_ORIGINS")
      );

      warnSpy.mockRestore();
    });
  });

  describe("createAuth - session and cookie configuration", () => {
    beforeEach(() => {
      mockGenericOAuth.mockClear();
      mockBetterAuth.mockClear();
      mockPrismaAdapter.mockClear();
    });

    it("should configure session expiresIn to 7 days (604800 seconds)", () => {
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        session: { expiresIn: number; updateAge: number };
      };
      expect(config.session.expiresIn).toBe(604800);
    });

    it("should configure session updateAge to 2 hours (7200 seconds)", () => {
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        session: { expiresIn: number; updateAge: number };
      };
      expect(config.session.updateAge).toBe(7200);
    });

    it("should configure BetterAuth database ID generation as UUID", () => {
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        advanced: {
          database: {
            generateId: string;
          };
        };
      };
      expect(config.advanced.database.generateId).toBe("uuid");
    });

    it("should set httpOnly cookie attribute to true", () => {
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        advanced: {
          defaultCookieAttributes: {
            httpOnly: boolean;
            secure: boolean;
            sameSite: string;
          };
        };
      };
      expect(config.advanced.defaultCookieAttributes.httpOnly).toBe(true);
    });

    it("should set sameSite cookie attribute to lax", () => {
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        advanced: {
          defaultCookieAttributes: {
            httpOnly: boolean;
            secure: boolean;
            sameSite: string;
          };
        };
      };
      expect(config.advanced.defaultCookieAttributes.sameSite).toBe("lax");
    });

    it("should set secure cookie attribute to true in production", () => {
      process.env.NODE_ENV = "production";
      process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        advanced: {
          defaultCookieAttributes: {
            httpOnly: boolean;
            secure: boolean;
            sameSite: string;
          };
        };
      };
      expect(config.advanced.defaultCookieAttributes.secure).toBe(true);
    });

    it("should set secure cookie attribute to false in non-production", () => {
      process.env.NODE_ENV = "development";
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        advanced: {
          defaultCookieAttributes: {
            httpOnly: boolean;
            secure: boolean;
            sameSite: string;
          };
        };
      };
      expect(config.advanced.defaultCookieAttributes.secure).toBe(false);
    });

    it("should set cookie domain when COOKIE_DOMAIN env var is present", () => {
      process.env.COOKIE_DOMAIN = ".mosaicstack.dev";
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        advanced: {
          defaultCookieAttributes: {
            httpOnly: boolean;
            secure: boolean;
            sameSite: string;
            domain?: string;
          };
        };
      };
      expect(config.advanced.defaultCookieAttributes.domain).toBe(".mosaicstack.dev");
    });

    it("should not set cookie domain when COOKIE_DOMAIN env var is absent", () => {
      delete process.env.COOKIE_DOMAIN;
      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as {
        advanced: {
          defaultCookieAttributes: {
            httpOnly: boolean;
            secure: boolean;
            sameSite: string;
            domain?: string;
          };
        };
      };
      expect(config.advanced.defaultCookieAttributes.domain).toBeUndefined();
    });
  });

  describe("getBetterAuthBaseUrl", () => {
    it("should prefer BETTER_AUTH_URL when set", () => {
      process.env.BETTER_AUTH_URL = "https://auth-base.example.com";
      process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";

      expect(getBetterAuthBaseUrl()).toBe("https://auth-base.example.com");
    });

    it("should fall back to NEXT_PUBLIC_API_URL when BETTER_AUTH_URL is not set", () => {
      process.env.NEXT_PUBLIC_API_URL = "https://api.example.com";

      expect(getBetterAuthBaseUrl()).toBe("https://api.example.com");
    });

    it("should throw when base URL is invalid", () => {
      process.env.BETTER_AUTH_URL = "not-a-url";

      expect(() => getBetterAuthBaseUrl()).toThrow("BetterAuth base URL must be a valid URL");
    });

    it("should throw when base URL is missing in production", () => {
      process.env.NODE_ENV = "production";

      expect(() => getBetterAuthBaseUrl()).toThrow("Missing BetterAuth base URL in production");
    });

    it("should throw when base URL is not https in production", () => {
      process.env.NODE_ENV = "production";
      process.env.BETTER_AUTH_URL = "http://api.example.com";

      expect(() => getBetterAuthBaseUrl()).toThrow(
        "BetterAuth base URL must use https in production"
      );
    });
  });

  describe("createAuth - baseURL wiring", () => {
    beforeEach(() => {
      mockBetterAuth.mockClear();
      mockPrismaAdapter.mockClear();
    });

    it("should pass BETTER_AUTH_URL into BetterAuth config", () => {
      process.env.BETTER_AUTH_URL = "https://api.mosaicstack.dev";

      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as { baseURL?: string };
      expect(config.baseURL).toBe("https://api.mosaicstack.dev");
    });

    it("should pass NEXT_PUBLIC_API_URL into BetterAuth config when BETTER_AUTH_URL is absent", () => {
      process.env.NEXT_PUBLIC_API_URL = "https://api.fallback.dev";

      const mockPrisma = {} as PrismaClient;
      createAuth(mockPrisma);

      expect(mockBetterAuth).toHaveBeenCalledOnce();
      const config = mockBetterAuth.mock.calls[0][0] as { baseURL?: string };
      expect(config.baseURL).toBe("https://api.fallback.dev");
    });
  });
});
