import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { createFeatureFlagClient } from './client.js';

describe('createFeatureFlagClient', () => {
  const baseConfig = {
    baseUrl: 'http://localhost:4003/api',
    productId: 'testapp',
    platform: 'web',
  };

  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
    vi.restoreAllMocks();
  });

  it('should create a client with isEnabled returning false before init', () => {
    const client = createFeatureFlagClient(baseConfig);
    expect(client.isEnabled('any_flag')).toBe(false);
  });

  it('should fetch flags on init', async () => {
    const mockFlags = { premium: true, beta_feature: false };
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ flags: mockFlags }),
      })
    );

    const client = createFeatureFlagClient(baseConfig);
    await client.init();

    expect(client.isEnabled('premium')).toBe(true);
    expect(client.isEnabled('beta_feature')).toBe(false);
    expect(client.isEnabled('nonexistent')).toBe(false);
    client.stop();
  });

  it('should return all flags via getAllFlags', async () => {
    const mockFlags = { a: true, b: false };
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ flags: mockFlags }),
      })
    );

    const client = createFeatureFlagClient(baseConfig);
    await client.init();

    expect(client.getAllFlags()).toEqual({ a: true, b: false });
    client.stop();
  });

  it('should send correct headers', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ flags: {} }),
    });
    vi.stubGlobal('fetch', fetchMock);

    const client = createFeatureFlagClient(baseConfig);
    await client.init({ userId: 'user-123' });

    expect(fetchMock).toHaveBeenCalledWith(
      expect.stringContaining('/flags/poll?'),
      expect.objectContaining({
        headers: expect.objectContaining({ 'x-product-id': 'testapp' }),
      })
    );

    const url = fetchMock.mock.calls[0][0] as string;
    expect(url).toContain('platform=web');
    expect(url).toContain('userId=user-123');
    client.stop();
  });

  it('should keep existing flags on network error', async () => {
    let callCount = 0;
    vi.stubGlobal(
      'fetch',
      vi.fn().mockImplementation(() => {
        callCount++;
        if (callCount === 1) {
          return Promise.resolve({
            ok: true,
            json: () => Promise.resolve({ flags: { initial: true } }),
          });
        }
        return Promise.reject(new Error('network'));
      })
    );

    const client = createFeatureFlagClient(baseConfig);
    await client.init();
    expect(client.isEnabled('initial')).toBe(true);

    await client.refresh();
    expect(client.isEnabled('initial')).toBe(true);
    client.stop();
  });

  it('should persist to storage when provided', async () => {
    const store: Record<string, string> = {};
    const storage = {
      getItem: (k: string) => store[k] ?? null,
      setItem: (k: string, v: string) => {
        store[k] = v;
      },
    };

    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ flags: { cached: true } }),
      })
    );

    const client = createFeatureFlagClient({ ...baseConfig, storage });
    await client.init();

    expect(store['testapp-feature-flags']).toBeDefined();
    expect(JSON.parse(store['testapp-feature-flags'])).toEqual({ cached: true });
    client.stop();
  });

  it('should restore flags from storage on creation', () => {
    const store: Record<string, string> = {
      'testapp-feature-flags': JSON.stringify({ restored: true }),
    };
    const storage = {
      getItem: (k: string) => store[k] ?? null,
      setItem: (k: string, v: string) => {
        store[k] = v;
      },
    };

    const client = createFeatureFlagClient({ ...baseConfig, storage });
    expect(client.isEnabled('restored')).toBe(true);
  });

  it('should stop polling on stop()', async () => {
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ flags: {} }),
      })
    );

    const client = createFeatureFlagClient({ ...baseConfig, pollIntervalMs: 1000 });
    await client.init();
    client.stop();

    expect(client.isEnabled('anything')).toBe(false);
    expect(client.getAllFlags()).toEqual({});
  });
});
