import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import MockAdapter from 'axios-mock-adapter';
import { apiClient } from '@/api/client';
import {
  deleteC2Config,
  executeC2,
  getC2Config,
  getC2Tasks,
  importC2,
  listCallbackHistory,
  listCallbacks,
  putC2Config,
  testC2Config,
} from '@/api/c2';

let mock: MockAdapter;

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

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

describe('getC2Config', () => {
  it('returns config on 200', async () => {
    mock.onGet('/engagements/1/c2-config').reply(200, {
      has_token: true,
      url: 'https://mythic.lab:7443',
      verify_tls: true,
    });
    const result = await getC2Config(1);
    expect(result).toEqual({ has_token: true, url: 'https://mythic.lab:7443', verify_tls: true });
  });

  it('returns null on 404', async () => {
    mock.onGet('/engagements/1/c2-config').reply(404);
    const result = await getC2Config(1);
    expect(result).toBeNull();
  });

  it('throws on other errors', async () => {
    mock.onGet('/engagements/1/c2-config').reply(503);
    await expect(getC2Config(1)).rejects.toThrow();
  });
});

describe('putC2Config', () => {
  it('sends PUT to correct URL with body', async () => {
    mock.onPut('/engagements/2/c2-config').reply(200, {
      has_token: true,
      url: 'https://mythic.lab:7443',
      verify_tls: false,
    });
    const result = await putC2Config(2, {
      url: 'https://mythic.lab:7443',
      api_token: 'secret',
      verify_tls: false,
    });
    expect(result.has_token).toBe(true);
    expect(result.verify_tls).toBe(false);
    const req = mock.history['put'][0];
    expect(req.url).toBe('/engagements/2/c2-config');
    const body = JSON.parse(req.data as string);
    expect(body.api_token).toBe('secret');
    expect(body.verify_tls).toBe(false);
  });
});

describe('deleteC2Config', () => {
  it('sends DELETE to correct URL', async () => {
    mock.onDelete('/engagements/3/c2-config').reply(204);
    await expect(deleteC2Config(3)).resolves.toBeUndefined();
    expect(mock.history['delete'][0].url).toBe('/engagements/3/c2-config');
  });
});

describe('testC2Config', () => {
  it('sends POST and returns test result', async () => {
    mock.onPost('/engagements/1/c2-config/test').reply(200, { ok: true, error: null });
    const result = await testC2Config(1);
    expect(result.ok).toBe(true);
    expect(result.error).toBeNull();
    expect(mock.history['post'][0].url).toBe('/engagements/1/c2-config/test');
  });

  it('returns error message when connection fails', async () => {
    mock
      .onPost('/engagements/1/c2-config/test')
      .reply(200, { ok: false, error: 'Connection refused' });
    const result = await testC2Config(1);
    expect(result.ok).toBe(false);
    expect(result.error).toBe('Connection refused');
  });
});

describe('listCallbacks', () => {
  it('sends GET and returns callbacks', async () => {
    mock.onGet('/engagements/1/c2/callbacks').reply(200, {
      callbacks: [
        {
          display_id: 1,
          active: true,
          host: 'WIN-TARGET',
          user: 'administrator',
          domain: 'lab.local',
          last_checkin: '2026-06-10T10:00:00',
        },
      ],
    });
    const result = await listCallbacks(1);
    expect(result.callbacks).toHaveLength(1);
    expect(result.callbacks[0].display_id).toBe(1);
    expect(result.callbacks[0].host).toBe('WIN-TARGET');
    expect(mock.history['get'][0].url).toBe('/engagements/1/c2/callbacks');
  });
});

describe('executeC2', () => {
  it('sends POST with callback_display_id and commands', async () => {
    mock.onPost('/simulations/5/c2/execute').reply(200, {
      tasks: [
        { id: 1, mythic_task_display_id: 42, command: 'whoami', status: 'submitted', completed: false },
      ],
    });
    const result = await executeC2(5, {
      callback_display_id: 1,
      commands: ['whoami'],
    });
    expect(result.tasks).toHaveLength(1);
    expect(result.tasks[0].command).toBe('whoami');
    const req = mock.history['post'][0];
    expect(req.url).toBe('/simulations/5/c2/execute');
    const body = JSON.parse(req.data as string);
    expect(body.callback_display_id).toBe(1);
    expect(body.commands).toEqual(['whoami']);
  });
});

describe('getC2Tasks', () => {
  it('GET /simulations/:id/c2/tasks returns tasks list', async () => {
    mock.onGet('/simulations/7/c2/tasks').reply(200, {
      tasks: [
        {
          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',
          created_at: '2026-06-10T10:00:00',
          completed_at: '2026-06-10T10:00:05',
        },
      ],
    });
    const result = await getC2Tasks(7);
    expect(result.tasks).toHaveLength(1);
    expect(result.tasks[0].status).toBe('completed');
    expect(result.tasks[0].output).toBe('NT AUTHORITY\\SYSTEM');
    expect(mock.history['get'][0].url).toBe('/simulations/7/c2/tasks');
  });
});

describe('listCallbackHistory', () => {
  it('GET with page/page_size params', async () => {
    mock.onGet('/engagements/1/c2/callbacks/2/history').reply(200, {
      tasks: [],
      total: 0,
      page: 1,
      page_size: 25,
    });
    const result = await listCallbackHistory(1, 2, { page: 1, pageSize: 25 });
    expect(result.total).toBe(0);
    const req = mock.history['get'][0];
    expect(req.url).toBe('/engagements/1/c2/callbacks/2/history');
    expect(req.params).toMatchObject({ page: 1, page_size: 25 });
  });
});

describe('importC2', () => {
  it('POST /simulations/:id/c2/import with task_display_ids', async () => {
    mock.onPost('/simulations/7/c2/import').reply(200, { imported: 3, skipped: 1 });
    const result = await importC2(7, {
      callback_display_id: 2,
      task_display_ids: [10, 11, 12, 13],
    });
    expect(result.imported).toBe(3);
    expect(result.skipped).toBe(1);
    const req = mock.history['post'][0];
    expect(req.url).toBe('/simulations/7/c2/import');
    const body = JSON.parse(req.data as string);
    expect(body.callback_display_id).toBe(2);
    expect(body.task_display_ids).toEqual([10, 11, 12, 13]);
  });
});
