/**
 * Unit Tests: config-loader.js
 * Tests blocking async configuration initialization pattern
 */

import * as configModule from '../config-loader.js';

const { loadConfig, getConfig, getDssHost, getDssPort, getStorybookUrl, __resetForTesting } = configModule;

describe('config-loader', () => {
  // Setup
  let originalFetch;

  beforeAll(() => {
    // Save original fetch
    originalFetch = global.fetch;
  });

  beforeEach(() => {
    // Reset module state for clean tests
    if (typeof __resetForTesting === 'function') {
      __resetForTesting();
    }
  });

  afterAll(() => {
    // Restore fetch
    global.fetch = originalFetch;
  });

  describe('loadConfig()', () => {
    test('fetches configuration from /api/config endpoint', async () => {
      const mockConfig = {
        dssHost: 'dss.test.com',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      await loadConfig();

      expect(global.fetch).toHaveBeenCalledWith('/api/config');
    });

    test('throws error if endpoint returns error', async () => {
      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: false,
          status: 500,
          statusText: 'Internal Server Error'
        })
      );

      await expect(loadConfig()).rejects.toThrow();
    });

    test('handles network errors gracefully', async () => {
      global.fetch = jest.fn(() =>
        Promise.reject(new Error('Network error'))
      );

      await expect(loadConfig()).rejects.toThrow('Network error');
    });

    test('prevents double-loading of config', async () => {
      const mockConfig = {
        dssHost: 'dss.test.com',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      await loadConfig();
      await loadConfig(); // Call twice

      // fetch should only be called once
      expect(global.fetch).toHaveBeenCalledTimes(1);
    });
  });

  describe('getConfig()', () => {
    test('returns configuration object after loading', async () => {
      const mockConfig = {
        dssHost: 'dss.example.com',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      await loadConfig();
      const config = getConfig();

      expect(config).toEqual(mockConfig);
    });

    test('throws error if called before loadConfig()', () => {
      // Create fresh module for this test
      expect(() => getConfig()).toThrow(/called before configuration was loaded/i);
    });
  });

  describe('getDssHost()', () => {
    test('returns dssHost from config', async () => {
      const mockConfig = {
        dssHost: 'dss.overbits.luz.uy',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      await loadConfig();
      const host = getDssHost();

      expect(host).toBe('dss.overbits.luz.uy');
    });
  });

  describe('getDssPort()', () => {
    test('returns dssPort from config as string', async () => {
      const mockConfig = {
        dssHost: 'localhost',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      await loadConfig();
      const port = getDssPort();

      expect(port).toBe('3456');
    });
  });

  describe('getStorybookUrl()', () => {
    test('builds path-based Storybook URL', async () => {
      const mockConfig = {
        dssHost: 'dss.overbits.luz.uy',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      // Mock window.location.protocol
      Object.defineProperty(window, 'location', {
        value: { protocol: 'https:' },
        writable: true
      });

      await loadConfig();
      const url = getStorybookUrl();

      expect(url).toBe('https://dss.overbits.luz.uy/storybook/');
    });

    test('uses HTTP when on http:// origin', async () => {
      const mockConfig = {
        dssHost: 'localhost',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      Object.defineProperty(window, 'location', {
        value: { protocol: 'http:' },
        writable: true
      });

      await loadConfig();
      const url = getStorybookUrl();

      expect(url).toBe('http://localhost/storybook/');
    });

    test('Storybook URL uses /storybook/ path (not port)', async () => {
      const mockConfig = {
        dssHost: 'dss.example.com',
        dssPort: '3456',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      Object.defineProperty(window, 'location', {
        value: { protocol: 'https:' },
        writable: true
      });

      await loadConfig();
      const url = getStorybookUrl();

      // Should NOT include port 6006
      expect(url).not.toContain(':6006');
      // Should include /storybook/ path
      expect(url).toContain('/storybook/');
    });
  });

  describe('Configuration Integration', () => {
    test('all getters work together', async () => {
      const mockConfig = {
        dssHost: 'dss.integration.test',
        dssPort: '4567',
        storybookPort: 6006
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      Object.defineProperty(window, 'location', {
        value: { protocol: 'https:' },
        writable: true
      });

      await loadConfig();

      // Verify all getters work
      expect(getDssHost()).toBe('dss.integration.test');
      expect(getDssPort()).toBe('4567');
      expect(getStorybookUrl()).toContain('dss.integration.test');
      expect(getStorybookUrl()).toContain('/storybook/');

      const config = getConfig();
      expect(config.dssHost).toBe('dss.integration.test');
    });
  });

  describe('Edge Cases', () => {
    test('handles empty response', async () => {
      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve({})
        })
      );

      await loadConfig();
      const config = getConfig();

      expect(config).toEqual({});
    });

    test('handles null values in response', async () => {
      const mockConfig = {
        dssHost: null,
        dssPort: null,
        storybookPort: null
      };

      global.fetch = jest.fn(() =>
        Promise.resolve({
          ok: true,
          json: () => Promise.resolve(mockConfig)
        })
      );

      await loadConfig();
      const config = getConfig();

      expect(config.dssHost).toBeNull();
    });
  });
});
