import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    usePage: vi.fn(() => ({
      url: '/admin/operations',
      props: {
        auth: { user: { id: 1, name: 'Admin', email: 'admin@test.com', is_admin: true } },
        features: {
          billing: false,
          socialAuth: false,
          emailVerification: true,
          apiTokens: true,
          userSettings: true,
          notifications: false,
          onboarding: false,
          apiDocs: false,
          twoFactor: false,
          webhooks: false,
          admin: true,
        },
      },
    })),
    Link: ({
      children,
      href,
      ...rest
    }: {
      children: React.ReactNode;
      href: string;
      className?: string;
    }) => (
      <a href={href} {...rest}>
        {children}
      </a>
    ),
    router: {
      patch: vi.fn(),
      delete: vi.fn(),
      post: vi.fn(),
      get: vi.fn(),
      visit: vi.fn(),
      on: vi.fn(() => vi.fn()),
    },
  };
});

vi.mock('@/Components/theme/use-theme', () => ({
  useTheme: vi.fn(() => ({ theme: 'system', setTheme: vi.fn(), resolvedTheme: 'light' })),
}));

import OperationsIndex from './Index';

const defaultProps = {
  active_jobs: {
    analysis_runs: 0,
    ai_jobs: 0,
    batch_jobs: 0,
  },
  queue_health: {
    pending: 0,
    failed_24h: 0,
    failed_1h: 0,
    oldest_pending_minutes: null,
    by_queue: [],
  },
  stuck_jobs: [],
  recent_failures: [],
  byok_health: {
    total: 0,
    valid: 0,
    invalid: 0,
    never_validated: 0,
    rate_limited: 0,
    quota_exceeded: 0,
    consecutive_failures_3plus: 0,
  },
  connection_health: {
    gsc: { total: 0, unhealthy: 0 },
    wp: { total: 0, unhealthy: 0 },
  },
};

describe('OperationsIndex', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('renders the Operations Dashboard heading', () => {
    render(<OperationsIndex {...defaultProps} />);
    expect(
      screen.getByRole('heading', { name: 'Operations Dashboard', level: 1 }),
    ).toBeInTheDocument();
  });

  it('shows "All systems operational" when everything is clean', () => {
    render(<OperationsIndex {...defaultProps} />);
    expect(screen.getByText('All systems operational')).toBeInTheDocument();
    expect(
      screen.getByText('No active jobs, failures, or stuck processes.'),
    ).toBeInTheDocument();
  });

  it('renders stat cards for active jobs, queue pending, failed, and stuck', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        active_jobs={{ analysis_runs: 2, ai_jobs: 3, batch_jobs: 1 }}
        queue_health={{ pending: 10, failed_24h: 0, failed_1h: 0, oldest_pending_minutes: null, by_queue: [] }}
      />,
    );
    expect(screen.getByText('Active Jobs')).toBeInTheDocument();
    expect(screen.getByText('Queue Pending')).toBeInTheDocument();
    expect(screen.getByText('Failed (24h)')).toBeInTheDocument();
    expect(screen.getByText('Stuck Jobs')).toBeInTheDocument();
  });

  it('shows breakdown cards for Analysis Runs, AI Jobs, and Batch Jobs', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        active_jobs={{ analysis_runs: 5, ai_jobs: 3, batch_jobs: 2 }}
      />,
    );
    // Each label appears in both the stat grid and the breakdown card grid
    expect(screen.getAllByText('Analysis Runs').length).toBeGreaterThanOrEqual(1);
    expect(screen.getAllByText('AI Jobs').length).toBeGreaterThanOrEqual(1);
    expect(screen.getAllByText('Batch Jobs').length).toBeGreaterThanOrEqual(1);
  });

  it('does not show stuck jobs section when there are no stuck jobs', () => {
    render(<OperationsIndex {...defaultProps} stuck_jobs={[]} />);
    expect(screen.queryByText(/Stuck Jobs \(/)).not.toBeInTheDocument();
  });

  it('shows stuck jobs section when there are stuck jobs', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        stuck_jobs={[
          { type: 'AnalysisRun', id: 42, site_name: 'Acme Blog', started_at: '2026-03-24T00:00:00Z', hours_stuck: 3 },
        ]}
      />,
    );
    expect(screen.getByText('Stuck Jobs (1)')).toBeInTheDocument();
    expect(screen.getByText('42')).toBeInTheDocument();
    expect(screen.getByText('Acme Blog')).toBeInTheDocument();
    expect(screen.getByText('3h')).toBeInTheDocument();
  });

  it('shows stuck job type badge', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        stuck_jobs={[
          { type: 'AnalysisRun', id: 1, site_name: null, started_at: null, hours_stuck: 2 },
        ]}
      />,
    );
    expect(screen.getByText('AnalysisRun')).toBeInTheDocument();
  });

  it('shows "—" for site_name when null', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        stuck_jobs={[
          { type: 'AiJob', id: 5, site_name: null, started_at: null, hours_stuck: 1 },
        ]}
      />,
    );
    expect(screen.getByText('—')).toBeInTheDocument();
  });

  it('does not show recent failures section when empty', () => {
    render(<OperationsIndex {...defaultProps} recent_failures={[]} />);
    expect(screen.queryByText(/Recent Failures \(/)).not.toBeInTheDocument();
  });

  it('shows recent failures when provided', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        recent_failures={[
          {
            uuid: 'abc-123',
            connection: 'database',
            queue: 'default',
            exception: 'Illuminate\\Queue\\MaxAttemptsExceededException',
            failed_at: '2026-03-24T10:00:00',
          },
        ]}
      />,
    );
    expect(screen.getByText('Recent Failures (1)')).toBeInTheDocument();
    expect(screen.getByText('default')).toBeInTheDocument();
    expect(screen.getByText(/MaxAttemptsExceededException/)).toBeInTheDocument();
  });

  it('does not show BYOK health card when total is 0', () => {
    render(<OperationsIndex {...defaultProps} byok_health={{ ...defaultProps.byok_health, total: 0 }} />);
    expect(screen.queryByText('BYOK AI Key Health')).not.toBeInTheDocument();
  });

  it('shows BYOK health card when total > 0', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        byok_health={{ total: 10, valid: 8, invalid: 1, never_validated: 1, rate_limited: 0, quota_exceeded: 0, consecutive_failures_3plus: 0 }}
      />,
    );
    expect(screen.getByText('BYOK AI Key Health')).toBeInTheDocument();
    expect(screen.getByText('Valid')).toBeInTheDocument();
    expect(screen.getByText('Invalid / Errors')).toBeInTheDocument();
    expect(screen.getByText('Never Validated')).toBeInTheDocument();
  });

  it('shows rate-limited badge in BYOK health when rate_limited > 0', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        byok_health={{ total: 5, valid: 3, invalid: 1, never_validated: 1, rate_limited: 2, quota_exceeded: 0, consecutive_failures_3plus: 0 }}
      />,
    );
    expect(screen.getByText('2 rate-limited')).toBeInTheDocument();
  });

  it('does not show connection health card when both connections have 0 total', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        connection_health={{ gsc: { total: 0, unhealthy: 0 }, wp: { total: 0, unhealthy: 0 } }}
      />,
    );
    expect(screen.queryByText('Connection Health')).not.toBeInTheDocument();
  });

  it('shows connection health card when GSC connections exist', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        connection_health={{
          gsc: { total: 5, unhealthy: 0 },
          wp: { total: 3, unhealthy: 1 },
        }}
      />,
    );
    expect(screen.getByText('Connection Health')).toBeInTheDocument();
    // "GSC Connections" and "WP Connections" appear in both nav and card
    expect(screen.getAllByText('GSC Connections').length).toBeGreaterThanOrEqual(1);
    expect(screen.getAllByText('WP Connections').length).toBeGreaterThanOrEqual(1);
  });

  it('shows "All healthy" when connections have no unhealthy entries', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        connection_health={{
          gsc: { total: 5, unhealthy: 0 },
          wp: { total: 3, unhealthy: 0 },
        }}
      />,
    );
    const allHealthy = screen.getAllByText('All healthy');
    expect(allHealthy).toHaveLength(2);
  });

  it('shows unhealthy count when connections are unhealthy', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        connection_health={{
          gsc: { total: 5, unhealthy: 2 },
          wp: { total: 3, unhealthy: 1 },
        }}
      />,
    );
    expect(screen.getByText('2 unhealthy')).toBeInTheDocument();
    expect(screen.getByText('1 unhealthy')).toBeInTheDocument();
  });

  it('does not show "All systems operational" when there are stuck jobs', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        stuck_jobs={[
          { type: 'AnalysisRun', id: 1, site_name: null, started_at: null, hours_stuck: 2 },
        ]}
      />,
    );
    expect(screen.queryByText('All systems operational')).not.toBeInTheDocument();
  });

  it('does not show "All systems operational" when there are failed jobs', () => {
    render(
      <OperationsIndex
        {...defaultProps}
        queue_health={{ pending: 0, failed_24h: 1, failed_1h: 0, oldest_pending_minutes: null, by_queue: [] }}
      />,
    );
    expect(screen.queryByText('All systems operational')).not.toBeInTheDocument();
  });
});
