// @vitest-environment happy-dom

import { describe, expect, it, beforeAll, beforeEach, afterEach, vi } from 'vitest';
import { act, createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';

import { ProductProvider, useProduct } from '@/lib/product-context';

beforeAll(() => {
  (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
});

beforeEach(() => {
  localStorage.clear();
});

afterEach(() => {
  vi.unstubAllGlobals();
});

function renderProductHarness() {
  const container = document.createElement('div');
  document.body.appendChild(container);
  let root!: Root;

  function Harness() {
    const { productId, productName, setProductId } = useProduct();
    return createElement(
      'div',
      null,
      createElement('span', { 'data-testid': 'product-id' }, productId),
      createElement('span', { 'data-testid': 'product-name' }, productName),
      createElement(
        'button',
        { type: 'button', onClick: () => setProductId('chronomind') },
        'ChronoMind'
      )
    );
  }

  act(() => {
    root = createRoot(container);
    root.render(createElement(ProductProvider, null, createElement(Harness)));
  });

  return { container, cleanup: () => act(() => root.unmount()) };
}

describe('ProductProvider', () => {
  it('dispatches product change events when the selected product changes', () => {
    const listener = vi.fn();
    window.addEventListener('tracker:product-changed', listener);
    const { container, cleanup } = renderProductHarness();

    act(() => {
      container.querySelector('button')?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
    });

    expect(localStorage.getItem('tracker_selected_product')).toBe('chronomind');
    expect(container.querySelector('[data-testid="product-id"]')?.textContent).toBe('chronomind');
    expect(listener).toHaveBeenCalledTimes(1);

    cleanup();
    window.removeEventListener('tracker:product-changed', listener);
  });

  it('syncs when settings updates localStorage and emits the shared product event', () => {
    const { container, cleanup } = renderProductHarness();

    act(() => {
      localStorage.setItem('tracker_selected_product', 'nomgap');
      window.dispatchEvent(new Event('tracker:product-changed'));
    });

    expect(container.querySelector('[data-testid="product-id"]')?.textContent).toBe('nomgap');
    expect(container.querySelector('[data-testid="product-name"]')?.textContent).toBe('NomGap');

    cleanup();
  });

  it('does not fetch /products/mine when there is no auth token (keeps the default list)', () => {
    const fetchSpy = vi.fn();
    vi.stubGlobal('fetch', fetchSpy);
    const { cleanup } = renderProductHarness();
    expect(fetchSpy).not.toHaveBeenCalled();
    cleanup();
  });

  it('replaces the list with the caller\u2019s projects from /products/mine when authed', async () => {
    localStorage.setItem('tracker_token', 'tok');
    localStorage.setItem('tracker_selected_product', 'acme');
    const fetchSpy = vi.fn(async () => ({
      ok: true,
      json: async () => ({ products: [{ productId: 'acme', displayName: 'Acme App' }] }),
    }));
    vi.stubGlobal('fetch', fetchSpy as unknown as typeof fetch);

    const container = document.createElement('div');
    document.body.appendChild(container);
    let root!: Root;
    function Harness() {
      const { products, productName } = useProduct();
      return createElement(
        'div',
        null,
        createElement('span', { 'data-testid': 'names' }, products.map(p => p.name).join(',')),
        createElement('span', { 'data-testid': 'name' }, productName)
      );
    }
    await act(async () => {
      root = createRoot(container);
      root.render(createElement(ProductProvider, null, createElement(Harness)));
    });
    await act(async () => {
      await Promise.resolve();
      await Promise.resolve();
    });

    expect(fetchSpy).toHaveBeenCalledWith(
      '/api/products/mine',
      expect.objectContaining({ headers: { Authorization: 'Bearer tok' } })
    );
    expect(container.querySelector('[data-testid="names"]')?.textContent).toBe('Acme App');
    expect(container.querySelector('[data-testid="name"]')?.textContent).toBe('Acme App');
    act(() => root.unmount());
  });
});
