import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MockAdapter from 'axios-mock-adapter';
import { apiClient } from '@/api/client';
import { MitreMatrixModal } from '@/components/MitreMatrixModal';
import { renderWithProviders } from './utils';
import type { MitreTactic, MitreTechnique } from '@/api/types';

const MATRIX: MitreTactic[] = [
  {
    tactic_id: 'TA0001',
    tactic_name: 'Initial Access',
    techniques: [
      {
        id: 'T1078',
        name: 'Valid Accounts',
        subtechniques: [
          { id: 'T1078.001', name: 'Default Accounts' },
          { id: 'T1078.002', name: 'Domain Accounts' },
        ],
      },
      {
        id: 'T1190',
        name: 'Exploit Public-Facing Application',
        subtechniques: [],
      },
    ],
  },
  {
    tactic_id: 'TA0002',
    tactic_name: 'Execution',
    techniques: [
      {
        id: 'T1059',
        name: 'Command and Scripting Interpreter',
        subtechniques: [{ id: 'T1059.001', name: 'PowerShell' }],
      },
    ],
  },
];

const SELECTION: MitreTechnique[] = [
  { id: 'T1078', name: 'Valid Accounts', tactics: ['initial-access'] },
];

const NO_TACTICS: never[] = [];

describe('MitreMatrixModal', () => {
  let mock: MockAdapter;

  beforeEach(() => {
    mock = new MockAdapter(apiClient);
    mock.onGet('/mitre/matrix').reply(200, MATRIX);
  });

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

  it('returns null when isOpen=false', () => {
    const { container } = renderWithProviders(
      <MitreMatrixModal
        isOpen={false}
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );
    expect(container.firstChild).toBeNull();
  });

  it('renders dialog with tactic columns when open', async () => {
    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );
    await waitFor(() => {
      expect(screen.getByText('Initial Access')).toBeInTheDocument();
      expect(screen.getByText('Execution')).toBeInTheDocument();
    });
  });

  it('renders techniques for each tactic', async () => {
    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );
    await waitFor(() => {
      expect(screen.getByText('T1078')).toBeInTheDocument();
      expect(screen.getByText('T1059')).toBeInTheDocument();
    });
  });

  it('Apply button calls onApply with selected techniques', async () => {
    const onApply = vi.fn();
    const user = userEvent.setup();

    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={onApply}
        onCancel={vi.fn()}
      />,
    );

    await waitFor(() => screen.getByText('T1078'));

    const t1078Btn = screen.getAllByRole('button').find(
      (btn) => btn.textContent?.includes('T1078') && !btn.getAttribute('aria-label'),
    );
    await user.click(t1078Btn!);

    await user.click(screen.getByRole('button', { name: /Appliquer/i }));

    expect(onApply).toHaveBeenCalledWith(
      expect.objectContaining({
        techniques: expect.arrayContaining([expect.objectContaining({ id: 'T1078' })]),
      }),
    );
  });

  it('Cancel button calls onCancel without onApply', async () => {
    const onApply = vi.fn();
    const onCancel = vi.fn();
    const user = userEvent.setup();

    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={onApply}
        onCancel={onCancel}
      />,
    );

    await user.click(screen.getByRole('button', { name: /Fermer/i }));

    expect(onCancel).toHaveBeenCalled();
    expect(onApply).not.toHaveBeenCalled();
  });

  it('Escape key calls onCancel', async () => {
    const onCancel = vi.fn();
    const user = userEvent.setup();

    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={onCancel}
      />,
    );

    await user.keyboard('{Escape}');

    expect(onCancel).toHaveBeenCalled();
  });

  it('shows initial technique selection as selected (count in header)', async () => {
    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={SELECTION}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );

    await waitFor(() => screen.getByText('T1078'));
    expect(screen.getByText(/1 sel\./i)).toBeInTheDocument();
  });

  it('search filter narrows visible techniques', async () => {
    const user = userEvent.setup();
    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );

    await waitFor(() => screen.getByText('T1078'));

    const searchInput = screen.getByPlaceholderText(/Filtrer/i);
    await user.type(searchInput, 'T1059');

    expect(screen.queryByText('T1078')).toBeNull();
    expect(screen.getByText('T1059')).toBeInTheDocument();
  });

  it('chevron expands subtechniques', async () => {
    const user = userEvent.setup();
    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );

    await waitFor(() => screen.getByText('T1078'));
    expect(screen.queryByText(/Default Accounts/)).toBeNull();

    const expandBtn = screen.getByRole('button', { name: /Expand T1078/i });
    await user.click(expandBtn);

    expect(screen.getByText(/Default Accounts/)).toBeInTheDocument();
  });

  it('Apply button shows item count when techniques selected', async () => {
    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={SELECTION}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByRole('button', { name: /Appliquer \(1 élément\)/i })).toBeInTheDocument();
    });
  });

  it('Apply button is disabled when nothing selected and no initial selection', async () => {
    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={vi.fn()}
      />,
    );

    await waitFor(() => screen.getByText('T1078'));

    const applyBtn = screen.getByRole('button', { name: /Tout effacer/i });
    expect(applyBtn).toBeDisabled();
  });

  it('Apply button shows "Tout effacer" and is enabled when initial selection is deselected', async () => {
    const onApply = vi.fn();
    const user = userEvent.setup();

    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={SELECTION}
        initialTactics={NO_TACTICS}
        onApply={onApply}
        onCancel={vi.fn()}
      />,
    );

    await waitFor(() => screen.getByText('T1078'));

    const t1078Btn = screen.getAllByRole('button').find(
      (btn) => btn.textContent?.includes('T1078') && !btn.getAttribute('aria-label'),
    );
    await user.click(t1078Btn!);

    const applyBtn = screen.getByRole('button', { name: /Tout effacer/i });
    expect(applyBtn).not.toBeDisabled();
    await user.click(applyBtn);
    expect(onApply).toHaveBeenCalledWith(
      expect.objectContaining({ techniques: [], tactics: [] }),
    );
  });

  it('backdrop click calls onCancel', async () => {
    const onCancel = vi.fn();
    const user = userEvent.setup();

    renderWithProviders(
      <MitreMatrixModal
        isOpen
        initialTechniques={[]}
        initialTactics={NO_TACTICS}
        onApply={vi.fn()}
        onCancel={onCancel}
      />,
    );

    const backdrop = document.querySelector('.modal-backdrop') as HTMLElement;
    if (backdrop) await user.click(backdrop);

    expect(onCancel).toHaveBeenCalled();
  });
});
