import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { screen, waitFor, fireEvent } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { apiClient } from '@/api/client';
import { C2TasksPanel } from '@/components/C2TasksPanel';
import { renderWithProviders } from '../utils';

vi.mock('@/hooks/useAuth', () => ({
  useAuth: () => ({
    user: { id: 1, username: 'alice', role: 'redteam', created_at: '2026-01-01' },
    status: 'authenticated',
    login: vi.fn(),
    logout: vi.fn(),
    isAdmin: false,
    isRedteam: true,
    isSoc: false,
    canEditEngagements: true,
  }),
}));

const COMPLETED_TASK = {
  id: 1,
  mythic_task_display_id: 10,
  callback_display_id: 1,
  command: 'whoami',
  params: null,
  status: 'completed',
  completed: true,
  output: 'NT AUTHORITY\\SYSTEM',
  mapping_applied: true,
  source: 'mimic' as const,
  created_at: '2026-06-10T10:00:00',
  completed_at: '2026-06-10T10:00:05',
};

const PENDING_TASK = {
  id: 2,
  mythic_task_display_id: 11,
  callback_display_id: 1,
  command: 'ipconfig',
  params: null,
  status: 'submitted',
  completed: false,
  output: null,
  mapping_applied: false,
  source: 'import' as const,
  created_at: '2026-06-10T10:00:10',
  completed_at: null,
};

let mock: MockAdapter;

beforeEach(() => {
  mock = new MockAdapter(apiClient);
});

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

describe('C2TasksPanel — empty state', () => {
  it('shows empty state copy when tasks array is empty', async () => {
    mock.onGet('/simulations/7/c2/tasks').reply(200, { tasks: [] });
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getByTestId('c2-tasks-panel')).toBeInTheDocument();
    });
    expect(screen.getByText(/No C2 tasks yet/i)).toBeInTheDocument();
    expect(screen.queryByTestId('c2-task-row')).toBeNull();
  });
});

describe('C2TasksPanel — populated rows', () => {
  beforeEach(() => {
    mock.onGet('/simulations/7/c2/tasks').reply(200, { tasks: [COMPLETED_TASK, PENDING_TASK] });
  });

  it('renders one row per task', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(2);
    });
  });

  it('displays task command and mythic display id', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getByText('whoami')).toBeInTheDocument();
      expect(screen.getByText('ipconfig')).toBeInTheDocument();
      expect(screen.getByText('#10')).toBeInTheDocument();
      expect(screen.getByText('#11')).toBeInTheDocument();
    });
  });

  it('shows MIMIC source badge for source=mimic', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getByText('MIMIC')).toBeInTheDocument();
    });
  });

  it('shows IMPORT source badge for source=import', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getByText('IMPORT')).toBeInTheDocument();
    });
  });

  it('shows completed_at timestamp for completed task', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getByText('2026-06-10T10:00:05')).toBeInTheDocument();
    });
  });

  it('shows em dash for null completed_at', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getByText('—')).toBeInTheDocument();
    });
  });
});

describe('C2TasksPanel — expand on click', () => {
  beforeEach(() => {
    mock.onGet('/simulations/7/c2/tasks').reply(200, { tasks: [COMPLETED_TASK] });
  });

  it('output row is hidden before click', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(1);
    });
    expect(screen.queryByTestId('c2-task-output')).toBeNull();
  });

  it('clicking a completed row reveals the output', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(1);
    });
    fireEvent.click(screen.getByTestId('c2-task-row'));
    expect(screen.getByTestId('c2-task-output')).toBeInTheDocument();
    expect(screen.getByTestId('c2-task-output')).toHaveTextContent('NT AUTHORITY\\SYSTEM');
  });

  it('clicking the expanded row collapses the output', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(1);
    });
    fireEvent.click(screen.getByTestId('c2-task-row'));
    expect(screen.getByTestId('c2-task-output')).toBeInTheDocument();
    fireEvent.click(screen.getByTestId('c2-task-row'));
    expect(screen.queryByTestId('c2-task-output')).toBeNull();
  });

  it('clicking an incomplete task row does not expand', async () => {
    mock.onGet('/simulations/7/c2/tasks').reply(200, { tasks: [PENDING_TASK] });
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(1);
    });
    fireEvent.click(screen.getByTestId('c2-task-row'));
    expect(screen.queryByTestId('c2-task-output')).toBeNull();
  });

  it('Enter key on completed row toggles output (a11y)', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(1);
    });
    const row = screen.getByTestId('c2-task-row');
    fireEvent.keyDown(row, { key: 'Enter' });
    expect(screen.getByTestId('c2-task-output')).toBeInTheDocument();
    fireEvent.keyDown(row, { key: 'Enter' });
    expect(screen.queryByTestId('c2-task-output')).toBeNull();
  });

  it('Space key on completed row toggles output (a11y)', async () => {
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(1);
    });
    const row = screen.getByTestId('c2-task-row');
    fireEvent.keyDown(row, { key: ' ' });
    expect(screen.getByTestId('c2-task-output')).toBeInTheDocument();
  });
});

describe('C2TasksPanel — refresh indicator', () => {
  it('does not show refresh indicator on initial load', async () => {
    mock.onGet('/simulations/7/c2/tasks').reply(200, { tasks: [] });
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getByTestId('c2-tasks-panel')).toBeInTheDocument();
    });
    // During isLoading, isFetching is true but isRefreshing = isFetching && !isLoading = false
    expect(screen.queryByTestId('c2-task-refresh-indicator')).toBeNull();
  });
});

describe('C2TasksPanel — polling behaviour', () => {
  it('does not refetch when all tasks are completed (refetchInterval false)', async () => {
    // With all completed tasks, refetchInterval returns false — only one GET call expected
    let callCount = 0;
    mock.onGet('/simulations/7/c2/tasks').reply(() => {
      callCount++;
      return [200, { tasks: [COMPLETED_TASK] }];
    });
    renderWithProviders(<C2TasksPanel simulationId={7} />);
    await waitFor(() => {
      expect(screen.getAllByTestId('c2-task-row')).toHaveLength(1);
    });
    // Wait a bit and confirm no extra fetches happened beyond initial
    await new Promise((r) => setTimeout(r, 100));
    expect(callCount).toBe(1);
  });
});
