import { http, HttpResponse } from 'msw';

import { server } from '@/setup-tests/server';

import { StackType } from '../../types';

import { duplicateStack } from './useDuplicateStackMutation';

type StackRequestBody = {
  name: string;
  swarmID: string;
  env?: Array<{ name: string; value: string }>;
};

describe('Swarm stack duplication', () => {
  it('should call getSwarm with targetEnvironmentId for Swarm stacks', async () => {
    const swarmId = 'swarm123';
    let swarmRequestCalled = false;

    server.use(
      http.get('/api/endpoints/:id/docker/swarm', ({ params }) => {
        swarmRequestCalled = true;
        expect(params.id).toBe('2');
        return HttpResponse.json({ ID: swarmId });
      }),
      http.post('/api/stacks/create/:type/:method', async () =>
        HttpResponse.json({ Id: 123 })
      )
    );

    await duplicateStack({
      name: 'test-stack',
      fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
      targetEnvironmentId: 2,
      type: StackType.DockerSwarm,
      env: [{ name: 'VAR1', value: 'value1' }],
    });

    expect(swarmRequestCalled).toBe(true);
  });

  it('should call createSwarmStackFromFileContent with correct parameters', async () => {
    const swarmId = 'swarm123';
    let stackRequestBody: undefined | StackRequestBody;

    server.use(
      http.get('/api/endpoints/:id/docker/swarm', () =>
        HttpResponse.json({ ID: swarmId })
      ),
      http.post('/api/stacks/create/:type/:method', async ({ request }) => {
        stackRequestBody = (await request.json()) as StackRequestBody;

        return HttpResponse.json({ Id: 123 });
      })
    );

    const fileContent = 'version: "3"\nservices:\n  app:\n    image: nginx';
    const env = [{ name: 'VAR1', value: 'value1' }];

    await duplicateStack({
      name: 'test-stack',
      fileContent,
      targetEnvironmentId: 2,
      type: StackType.DockerSwarm,
      env,
    });

    expect(stackRequestBody).toBeDefined();
    expect(stackRequestBody?.name).toBe('test-stack');
    expect(stackRequestBody?.swarmID).toBe(swarmId);
  });

  it('should throw error if Swarm ID is missing', async () => {
    server.use(
      http.get('/api/endpoints/:id/docker/swarm', () =>
        HttpResponse.json({ ID: '' })
      )
    );

    await expect(
      duplicateStack({
        name: 'test-stack',
        fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
        targetEnvironmentId: 2,
        type: StackType.DockerSwarm,
      })
    ).rejects.toThrow('Swarm ID is required');
  });

  it('should pass swarmID from getSwarm response', async () => {
    const swarmId = 'custom-swarm-id-456';
    let stackRequestBody: StackRequestBody | undefined;

    server.use(
      http.get('/api/endpoints/:id/docker/swarm', () =>
        HttpResponse.json({ ID: swarmId })
      ),
      http.post('/api/stacks/create/:type/:method', async ({ request }) => {
        stackRequestBody = (await request.json()) as StackRequestBody;
        return HttpResponse.json({ Id: 123 });
      })
    );

    await duplicateStack({
      name: 'test-stack',
      fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
      targetEnvironmentId: 2,
      type: StackType.DockerSwarm,
    });

    expect(stackRequestBody?.swarmID).toBe(swarmId);
  });

  it('should pass environmentId, name, stackFileContent, env', async () => {
    const swarmId = 'swarm123';
    let stackRequestBody: StackRequestBody | undefined;

    server.use(
      http.get('/api/endpoints/:id/docker/swarm', () =>
        HttpResponse.json({ ID: swarmId })
      ),
      http.post('/api/stacks/create/:type/:method', async ({ request }) => {
        stackRequestBody = (await request.json()) as StackRequestBody;
        return HttpResponse.json({ Id: 123 });
      })
    );

    const fileContent = 'version: "3"\nservices:\n  app:\n    image: nginx';
    const env = [
      { name: 'VAR1', value: 'value1' },
      { name: 'VAR2', value: 'value2' },
    ];

    await duplicateStack({
      name: 'my-swarm-stack',
      fileContent,
      targetEnvironmentId: 3,
      type: StackType.DockerSwarm,
      env,
    });

    expect(stackRequestBody).toBeDefined();
    expect(stackRequestBody?.name).toBe('my-swarm-stack');
    expect(stackRequestBody?.env).toEqual(env);
  });
});

describe('Standalone stack duplication', () => {
  it('should call createStandaloneStackFromFileContent for non-Swarm stacks', async () => {
    let stackRequestBody: StackRequestBody | undefined;

    server.use(
      http.post('/api/stacks/create/:type/:method', async ({ request }) => {
        stackRequestBody = (await request.json()) as StackRequestBody;
        return HttpResponse.json({ Id: 123 });
      })
    );

    await duplicateStack({
      name: 'standalone-stack',
      fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
      targetEnvironmentId: 2,
      type: StackType.DockerCompose,
    });

    expect(stackRequestBody).toBeDefined();
    expect(stackRequestBody?.name).toBe('standalone-stack');
  });

  it('should pass environmentId, name, stackFileContent, env', async () => {
    let stackRequestBody: StackRequestBody | undefined;

    server.use(
      http.post('/api/stacks/create/:type/:method', async ({ request }) => {
        stackRequestBody = (await request.json()) as StackRequestBody;
        return HttpResponse.json({ Id: 123 });
      })
    );

    const fileContent = 'version: "3"\nservices:\n  web:\n    image: nginx';
    const env = [{ name: 'PORT', value: '8080' }];

    await duplicateStack({
      name: 'compose-stack',
      fileContent,
      targetEnvironmentId: 5,
      type: StackType.DockerCompose,
      env,
    });

    expect(stackRequestBody).toBeDefined();
    expect(stackRequestBody?.name).toBe('compose-stack');
    expect(stackRequestBody?.env).toEqual(env);
  });

  it('should not call getSwarm for standalone stacks', async () => {
    let swarmRequestCalled = false;

    server.use(
      http.get('/api/endpoints/:id/docker/swarm', () => {
        swarmRequestCalled = true;
        return HttpResponse.json({ ID: 'swarm123' });
      }),
      http.post('/api/stacks/create/:type/:method', () =>
        HttpResponse.json({ Id: 123 })
      )
    );

    await duplicateStack({
      name: 'standalone-stack',
      fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
      targetEnvironmentId: 2,
      type: StackType.DockerCompose,
    });

    expect(swarmRequestCalled).toBe(false);
  });
});

describe('error handling', () => {
  it('should propagate errors from getSwarm', async () => {
    server.use(
      http.get('/api/endpoints/:id/docker/swarm', () =>
        HttpResponse.json({ message: 'Swarm not found' }, { status: 404 })
      )
    );

    await expect(
      duplicateStack({
        name: 'test-stack',
        fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
        targetEnvironmentId: 2,
        type: StackType.DockerSwarm,
      })
    ).rejects.toThrow();
  });

  it('should propagate errors from createSwarmStackFromFileContent', async () => {
    server.use(
      http.get('/api/endpoints/:id/docker/swarm', () =>
        HttpResponse.json({ ID: 'swarm123' })
      ),
      http.post('/api/stacks/create/:type/:method', () =>
        HttpResponse.json({ message: 'Stack creation failed' }, { status: 500 })
      )
    );

    await expect(
      duplicateStack({
        name: 'test-stack',
        fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
        targetEnvironmentId: 2,
        type: StackType.DockerSwarm,
      })
    ).rejects.toThrow();
  });

  it('should propagate errors from createStandaloneStackFromFileContent', async () => {
    server.use(
      http.post('/api/stacks/create/:type/:method', () =>
        HttpResponse.json(
          { message: 'Stack name already exists' },
          { status: 409 }
        )
      )
    );

    await expect(
      duplicateStack({
        name: 'existing-stack',
        fileContent: 'version: "3"\nservices:\n  app:\n    image: nginx',
        targetEnvironmentId: 2,
        type: StackType.DockerCompose,
      })
    ).rejects.toThrow();
  });
});
