import { afterEach, describe, expect, it, vi } from 'vitest';
import { api } from './api';

type JsonRecord = Record<string, unknown>;

function jsonResponse(body: JsonRecord[] | JsonRecord) {
  return new Response(JSON.stringify(body), {
    status: 200,
    headers: {
      'Content-Type': 'application/json'
    }
  });
}

describe('api fetch injection', () => {
  const originalFetch = globalThis.fetch;

  afterEach(() => {
    globalThis.fetch = originalFetch;
  });

  it.each([
    {
      label: 'raw materials',
      call: (fetcher: typeof fetch) => api.rawMaterials(fetcher),
      path: '/api/raw-materials',
      body: [{ id: 1, name: 'Lime' }]
    },
    {
      label: 'mixes',
      call: (fetcher: typeof fetch) => api.mixes(fetcher),
      path: '/api/mixes',
      body: [{ id: 12, name: 'Orchard Blend' }]
    },
    {
      label: 'products',
      call: (fetcher: typeof fetch) => api.products(fetcher),
      path: '/api/products',
      body: [{ id: 8, name: 'Packhouse Bag' }]
    },
    {
      label: 'product costs',
      call: (fetcher: typeof fetch) => api.productCosts(fetcher),
      path: '/api/powerbi/product-costs',
      body: [{ product_id: 8, finished_product_delivered: 14.2 }]
    }
  ])('uses the injected fetch for $label reads', async ({ call, path, body }) => {
    const globalFetch = vi.fn(() => {
      throw new Error('global fetch should not be used');
    }) as typeof fetch;
    const injectedFetch = vi.fn(async () => jsonResponse(body)) as typeof fetch;

    globalThis.fetch = globalFetch;

    await expect(call(injectedFetch)).resolves.toEqual(body);
    expect(injectedFetch).toHaveBeenCalledTimes(1);
    expect(injectedFetch.mock.calls[0]?.[0]).toBe(path);
    expect(globalFetch).not.toHaveBeenCalled();
  });

  it('shows a backend-unavailable message for login network failures', async () => {
    globalThis.fetch = vi.fn(async () => {
      throw new TypeError('Failed to fetch');
    }) as typeof fetch;

    await expect(api.clientLogin('user@example.com', 'secret')).rejects.toThrow(
      'Unable to reach the server. Check that the backend is running and try again.'
    );
  });

  it('shows a backend-unavailable message for authenticated read network failures', async () => {
    const injectedFetch = vi.fn(async () => {
      throw new TypeError('NetworkError when attempting to fetch resource.');
    }) as typeof fetch;

    await expect(api.rawMaterials(injectedFetch)).rejects.toThrow(
      'Unable to reach the server. Check that the backend is running and try again.'
    );
  });
});
