import React from 'react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import type { MaintenanceHistoryEntry } from '@harness-engineering/types';
import { Maintenance } from '../../../src/client/pages/Maintenance';

// Mock socket hook
const mockSocket = {
  snapshot: null,
  interactions: [],
  agentEvents: {},
  maintenanceEvent: null,
  connected: true,
  removeInteraction: vi.fn(),
  setInteractions: vi.fn(),
};

vi.mock('../../../src/client/hooks/useOrchestratorSocket', () => ({
  useOrchestratorSocket: () => mockSocket,
}));

const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch);

// Mocks must match the real wire shape produced by orchestrator's
// toMaintenanceHistoryEntry serializer (GET /api/maintenance/history).
function mockApi(history: MaintenanceHistoryEntry[]) {
  mockFetch.mockImplementation((url: string) => {
    if (url.endsWith('/api/maintenance/status')) {
      return Promise.resolve({
        ok: true,
        json: async () => ({
          scheduledTasks: 20,
          lastRunAt: null,
          nextRunAt: null,
          running: false,
        }),
      });
    }
    if (url.endsWith('/api/maintenance/history')) {
      return Promise.resolve({
        ok: true,
        json: async () => history,
      });
    }
    return Promise.resolve({ ok: false, status: 404 });
  });
}

beforeEach(() => {
  mockFetch.mockReset();
});

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

describe('Maintenance page — candidate-count badge', () => {
  it('shows the badge for compound-candidates rows when findings > 0', async () => {
    mockApi([
      {
        task: 'compound-candidates',
        status: 'success',
        startedAt: '2026-05-05T09:00:00Z',
        durationMs: 1234,
        findings: 5,
        prUrl: null,
      },
    ]);

    render(<Maintenance />);

    await waitFor(() => {
      expect(screen.getByText(/5 candidates/i)).toBeDefined();
    });
  });

  it('hides the badge for compound-candidates when findings is 0', async () => {
    mockApi([
      {
        task: 'compound-candidates',
        status: 'success',
        startedAt: '2026-05-05T09:00:00Z',
        durationMs: 1234,
        findings: 0,
        prUrl: null,
      },
    ]);

    render(<Maintenance />);

    await waitFor(() => {
      expect(screen.getByText('compound-candidates')).toBeDefined();
    });
    expect(screen.queryByText(/^\d+ candidates$/)).toBeNull();
  });

  it('hides the badge for product-pulse rows even when findings > 0 (badge scoped to compound-candidates)', async () => {
    mockApi([
      {
        task: 'product-pulse',
        status: 'success',
        startedAt: '2026-05-05T08:00:00Z',
        durationMs: 1234,
        findings: 5,
        prUrl: null,
      },
    ]);

    render(<Maintenance />);

    await waitFor(() => {
      expect(screen.getByText('product-pulse')).toBeDefined();
    });
    expect(screen.queryByText(/5 candidates/i)).toBeNull();
  });

  it('hides the badge when findings field is missing entirely (runtime guard)', async () => {
    // The wire contract makes `findings` required, but the production
    // predicate uses `(entry.findings ?? 0) > 0` as defense-in-depth
    // against malformed responses. Cast to exercise that guard.
    mockApi([
      {
        task: 'compound-candidates',
        status: 'success',
        startedAt: '2026-05-05T09:00:00Z',
        durationMs: 1234,
        prUrl: null,
      } as unknown as MaintenanceHistoryEntry,
    ]);

    render(<Maintenance />);

    await waitFor(() => {
      expect(screen.getByText('compound-candidates')).toBeDefined();
    });
    expect(screen.queryByText(/^\d+ candidates$/)).toBeNull();
  });
});

describe('Maintenance page — statusAccent color mapping', () => {
  // Regression coverage for statusAccent() in Maintenance.tsx: the function
  // has 3 branches (success|no-issues -> emerald, failed -> red, fallthrough
  // yellow for skipped). Without this test, a future change could silently
  // drop a status color (e.g. removing 'no-issues' from the emerald branch).
  it.each([
    ['success', 'text-emerald-400'],
    ['no-issues', 'text-emerald-400'],
    ['failed', 'text-red-400'],
    ['skipped', 'text-yellow-400'],
  ] as const)('renders status %s with class %s', async (status, expectedClass) => {
    mockApi([
      {
        task: 'product-pulse',
        status,
        startedAt: '2026-05-05T08:00:00Z',
        durationMs: 1234,
        findings: 0,
        prUrl: null,
      },
    ]);

    render(<Maintenance />);

    const statusCell = await waitFor(() => screen.getByText(status));
    expect(statusCell.className).toContain(expectedClass);
  });
});
