// @vitest-environment happy-dom
/**
 * Fleet overview page (§1) — the consolidated operations surface.
 *
 * Verifies the panels that surface the §2/§3 signal: the engine circuit-breaker
 * panel, the budget guardrail, and the dead-letter triage list with its Re-drive
 * button (which calls the operator action). Uses the jsdom render harness +
 * mocked fleet-client/auth-context (no network).
 */

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

const { operatorAction, listFactories, getFleetMetrics, listJobs } = vi.hoisted(() => ({
  operatorAction: vi.fn(),
  listFactories: vi.fn(),
  getFleetMetrics: vi.fn(),
  listJobs: vi.fn(),
}));

vi.mock('@/lib/auth-context', () => ({ useAuth: () => ({ token: 'tok' }) }));
vi.mock('@/lib/fleet-client', () => ({
  listFactories,
  getFleetMetrics,
  listJobs,
  operatorAction,
}));

import FleetOverviewPage from '@/app/dashboard/fleet/page';

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

const metricsBase = {
  productId: 'p',
  generatedAt: '2026-06-01T00:00:00.000Z',
  jobs: { total: 1, byStage: {}, queueDepth: 0, blocked: 0, active: 0, oldestQueuedAgeMs: null },
  factories: {
    total: 0,
    live: 0,
    stale: 0,
    byHealth: { ok: 0, degraded: 0, down: 0 },
    seatsUsed: 0,
    seatsTotal: 0,
    utilizationPct: 0,
  },
  alerts: [],
};

const deadLetterJob = {
  id: 'j-dead',
  productId: 'p',
  stage: 'dead_letter',
  idempotencyKey: 'broken-task',
  bodyMd: '',
  priority: 'medium',
  priorityOrder: 2,
  capabilities: [],
  kind: 'leaf',
  attempts: 3,
  leaseEpoch: 3,
  engine: 'codex',
  createdAt: '2026-06-01T00:00:00.000Z',
  updatedAt: '2026-06-01T00:00:00.000Z',
};

async function renderPage(): Promise<{ root: Root; container: HTMLDivElement }> {
  const container = document.createElement('div');
  document.body.appendChild(container);
  let root!: Root;
  await act(async () => {
    root = createRoot(container);
    root.render(createElement(FleetOverviewPage));
  });
  // Flush the async refresh() (Promise.all of the mocked client calls).
  await act(async () => {
    await Promise.resolve();
    await Promise.resolve();
  });
  return { root, container };
}

beforeEach(() => {
  vi.clearAllMocks();
  listFactories.mockResolvedValue({ factories: [] });
  operatorAction.mockResolvedValue({});
  // Default: recent jobs empty, dead-letter query returns the broken job.
  listJobs.mockImplementation(async (params?: { stage?: string }) =>
    params?.stage === 'dead_letter' ? { jobs: [deadLetterJob] } : { jobs: [] }
  );
});

describe('fleet overview §1 — consolidated panels', () => {
  it('renders the budget guardrail with projection and per-engine breakdown', async () => {
    getFleetMetrics.mockResolvedValue({
      ...metricsBase,
      budget: {
        ceilingUsd: 100,
        spentUsd: 40,
        status: 'active',
        window: 'monthly',
        projectedUsd: 400,
        engines: [{ engine: 'codex', spentUsd: 30, ceilingUsd: 30, exhausted: true }],
      },
    });
    const { root, container } = await renderPage();
    const budget = container.querySelector('[data-testid="fleet-budget"]');
    expect(budget).not.toBeNull();
    expect(budget!.textContent).toContain('$40.00 / $100.00');
    expect(budget!.textContent).toContain('Projected monthly: $400.00');
    expect(budget!.textContent).toContain('codex');
    act(() => root.unmount());
    container.remove();
  });

  it('renders only tripped circuit breakers', async () => {
    getFleetMetrics.mockResolvedValue({
      ...metricsBase,
      engineBreakers: [
        {
          factoryId: 'fac_1',
          engine: 'codex',
          state: 'OPEN',
          failureCount: 3,
          lastFailureAt: null,
        },
        {
          factoryId: 'fac_1',
          engine: 'devin',
          state: 'CLOSED',
          failureCount: 0,
          lastFailureAt: null,
        },
      ],
    });
    const { root, container } = await renderPage();
    const panel = container.querySelector('[data-testid="fleet-breakers"]');
    expect(panel).not.toBeNull();
    expect(panel!.textContent).toContain('fac_1 · codex');
    expect(panel!.textContent).toContain('OPEN');
    expect(panel!.textContent).not.toContain('devin'); // CLOSED pairs are hidden
    act(() => root.unmount());
    container.remove();
  });

  it('lists dead-letter jobs and re-drives one via the operator action', async () => {
    getFleetMetrics.mockResolvedValue({ ...metricsBase });
    const { root, container } = await renderPage();
    const triage = container.querySelector('[data-testid="fleet-dead-letter"]');
    expect(triage).not.toBeNull();
    expect(triage!.textContent).toContain('broken-task');

    const btn = container.querySelector(
      '[aria-label="Re-drive job broken-task"]'
    ) as HTMLButtonElement;
    expect(btn).not.toBeNull();
    await act(async () => {
      btn.click();
      await Promise.resolve();
    });
    expect(operatorAction).toHaveBeenCalledWith('j-dead', 'redrive');
    act(() => root.unmount());
    container.remove();
  });

  it('hides the budget and breaker panels when the metrics omit them', async () => {
    getFleetMetrics.mockResolvedValue({ ...metricsBase });
    listJobs.mockResolvedValue({ jobs: [] }); // no dead letters either
    const { root, container } = await renderPage();
    expect(container.querySelector('[data-testid="fleet-budget"]')).toBeNull();
    expect(container.querySelector('[data-testid="fleet-breakers"]')).toBeNull();
    expect(container.querySelector('[data-testid="fleet-dead-letter"]')).toBeNull();
    act(() => root.unmount());
    container.remove();
  });
});
