/**
 * StorageMonitor render tests.
 *
 * Strategy: seed the snippet + dataset stores (the measured inputs) and mock
 * readOriginUsage (the async browser adapter) so tests run in happy-dom without a
 * real Storage Manager, then assert on the rendered DOM — legend text, the
 * decorative bar, and the absence of a meter/threshold-warning (the redesign).
 *
 * CSS Modules are identity-mapped under Vitest, so class tokens come through as-is.
 */

import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import type { Snippet } from '@core/snippet';
import type { Dataset } from '@core/dataset';
import StorageMonitor from './StorageMonitor';
import { useSnippetStore } from '../stores/SnippetStore';
import { useDatasetStore } from '../stores/DatasetStore';

// Mock the browser adapter — tests must not touch navigator.storage.
vi.mock('../infrastructure/storage-estimate', () => ({
  readOriginUsage: vi.fn(),
}));
import { readOriginUsage } from '../infrastructure/storage-estimate';
const mockUsage = vi.mocked(readOriginUsage);

(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;

// ---- fixtures ---------------------------------------------------------------

/** A snippet is just serialized for its byte size, so a minimal shape suffices. */
function seedSnippets(count: number): void {
  const snippets = Array.from(
    { length: count },
    (_, i) => ({ id: `s${i}`, name: `snippet ${i}`, draftSpec: '{"x":1}' }) as unknown as Snippet,
  );
  useSnippetStore.setState({ snippets });
}

/** Datasets carry their own byte `size`; only that field is read. */
function seedDatasets(...sizes: number[]): void {
  const datasets = sizes.map(
    (size, i) => ({ id: i + 1, name: `data ${i}`, size }) as unknown as Dataset,
  );
  useDatasetStore.setState({ datasets });
}

// ---- test setup -------------------------------------------------------------

let container: HTMLDivElement;
let root: Root;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
  root = createRoot(container);
});

afterEach(() => {
  act(() => root.unmount());
  container.remove();
  useSnippetStore.setState({ snippets: [] });
  useDatasetStore.setState({ datasets: [] });
  vi.clearAllMocks();
});

async function renderMonitor() {
  await act(async () => {
    root.render(<StorageMonitor />);
    await Promise.resolve(); // flush the async readOriginUsage() resolution
  });
}

// ---- tests ------------------------------------------------------------------

const MB = 1024 * 1024;

describe('StorageMonitor (composition)', () => {
  test('shows Snippets, Datasets and App segments once past the user-data floor', async () => {
    seedSnippets(2);
    seedDatasets(12 * MB); // 12 MB of datasets — over the 10 MB floor
    mockUsage.mockResolvedValue(20 * MB);
    await renderMonitor();

    const root_ = container.querySelector('[aria-label="Storage in use"]');
    expect(root_).not.toBeNull();
    const legend = root_!.textContent ?? '';
    expect(legend).toContain('Snippets');
    expect(legend).toContain('Datasets');
    expect(legend).toContain('App'); // usage − snippets − datasets
    expect(legend).toContain('in use');
  });

  test('is not a meter and shows no threshold warning (redesign)', async () => {
    seedSnippets(1);
    seedDatasets(11 * MB);
    mockUsage.mockResolvedValue(20 * MB);
    await renderMonitor();

    expect(container.querySelector('[role="meter"]')).toBeNull();
    expect(container.querySelector('[role="status"]')).toBeNull();
    // The bar itself is decorative.
    expect(container.querySelector('[aria-hidden="true"]')).not.toBeNull();
  });

  test('omits the App segment when the Storage Manager API is unavailable', async () => {
    seedSnippets(1);
    seedDatasets(11 * MB);
    mockUsage.mockResolvedValue(undefined); // API absent
    await renderMonitor();

    const text = container.textContent ?? '';
    expect(text).toContain('Snippets');
    expect(text).toContain('Datasets');
    expect(text).not.toContain('App');
  });

  test('stays hidden below the user-data floor, even with origin usage', async () => {
    seedSnippets(3);
    seedDatasets(2 * MB); // ~2 MB of user data — under the 10 MB floor
    mockUsage.mockResolvedValue(9 * MB);
    await renderMonitor();
    expect(container.firstElementChild).toBeNull();
  });

  test('renders nothing for a truly empty workspace', async () => {
    seedSnippets(0);
    seedDatasets();
    mockUsage.mockResolvedValue(undefined);
    await renderMonitor();
    expect(container.firstElementChild).toBeNull();
  });
});
