import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';

import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
import { withTestRouter } from '@/react/test-utils/withRouter';
import { withUserProvider } from '@/react/test-utils/withUserProvider';

import {
  useHelmRepoVersions,
  ChartVersion,
} from '../../queries/useHelmRepoVersions';
import { HelmRelease } from '../../types';

import { openUpgradeHelmModal } from './UpgradeHelmModal';
import { UpgradeButton } from './UpgradeButton';

// Mock the upgrade modal function
vi.mock('./UpgradeHelmModal', () => ({
  openUpgradeHelmModal: vi.fn(() => Promise.resolve(undefined)),
}));

// Mock the notifications service
vi.mock('@/portainer/services/notifications', () => ({
  notifySuccess: vi.fn(),
}));

vi.mock('../../queries/useHelmRegistries', () => ({
  useHelmRegistries: vi.fn(() => ({
    data: ['repo1', 'repo2'],
    isInitialLoading: false,
    isError: false,
  })),
}));

vi.mock('../../queries/useHelmRepoVersions', () => ({
  useHelmRepoVersions: vi.fn(),
}));

// Mock the useHelmRelease hook
vi.mock('../queries/useHelmRelease', () => ({
  useHelmRelease: vi.fn(() => ({
    data: '1.0.0',
  })),
}));

// Mock the useUpdateHelmReleaseMutation hook
vi.mock('../../queries/useUpdateHelmReleaseMutation', () => ({
  useUpdateHelmReleaseMutation: vi.fn(() => ({
    mutate: vi.fn(),
    isLoading: false,
  })),
}));

function renderButton(props = {}) {
  const defaultProps = {
    environmentId: 1,
    releaseName: 'test-release',
    namespace: 'default',
    release: {
      name: 'test-release',
      chart: {
        metadata: {
          name: 'test-chart',
          version: '1.0.0',
        },
      },
      values: {
        userSuppliedValues: '{}',
      },
      manifest: '',
    } as HelmRelease,
    updateRelease: vi.fn(),
    ...props,
  };

  const Wrapped = withTestQueryProvider(
    withUserProvider(withTestRouter(UpgradeButton))
  );
  return render(<Wrapped {...defaultProps} />);
}

describe('UpgradeButton', () => {
  beforeEach(() => {
    // Set up default mock return values
    vi.mocked(useHelmRepoVersions).mockReturnValue({
      data: [
        { Version: '1.0.0', Repo: 'stable' },
        { Version: '1.1.0', Repo: 'stable' },
      ],
      isInitialLoading: false,
      isError: false,
      isFetching: false,
      refetch: vi.fn(() => Promise.resolve([])),
    });
  });

  test('should display the upgrade button', () => {
    renderButton();

    const button = screen.getByRole('button', { name: /Upgrade/i });
    expect(button).toBeInTheDocument();
  });

  test('should be disabled when no versions are available', () => {
    const data: ChartVersion[] = [];
    vi.mocked(useHelmRepoVersions).mockReturnValue({
      data,
      isInitialLoading: false,
      isError: false,
      isFetching: false,
      refetch: vi.fn(() => Promise.resolve([])),
    });

    renderButton();

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

  test('should show loading state when checking for versions', () => {
    vi.mocked(useHelmRepoVersions).mockReturnValue({
      data: [],
      isInitialLoading: true,
      isError: false,
      isFetching: true,
      refetch: vi.fn(() => Promise.resolve([])),
    });

    renderButton();

    expect(
      screen.getByText('Checking for new versions...')
    ).toBeInTheDocument();
  });

  test('should show "No versions available" when no versions are found', () => {
    const data: ChartVersion[] = [];
    vi.mocked(useHelmRepoVersions).mockReturnValue({
      data,
      isInitialLoading: false,
      isError: false,
      isFetching: false,
      refetch: vi.fn(() => Promise.resolve([])),
    });

    renderButton();

    expect(screen.getByText('No versions available')).toBeInTheDocument();
  });

  test('should open upgrade modal when clicked', async () => {
    const user = userEvent.setup();
    const mockRelease = {
      name: 'test-release',
      chart: {
        metadata: {
          name: 'test-chart',
          version: '1.0.0',
        },
      },
      values: {
        userSuppliedValues: '{}',
      },
      manifest: '',
    } as HelmRelease;

    vi.mocked(useHelmRepoVersions).mockReturnValue({
      data: [
        { Version: '1.0.0', Repo: 'stable' },
        { Version: '1.1.0', Repo: 'stable' },
      ],
      isInitialLoading: false,
      isError: false,
      isFetching: false,
      refetch: vi.fn(() => Promise.resolve([])),
    });

    renderButton({ release: mockRelease });

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

    await waitFor(() => {
      expect(openUpgradeHelmModal).toHaveBeenCalledWith(
        expect.objectContaining({
          name: 'test-release',
          chart: 'test-chart',
          namespace: 'default',
          values: '{}',
          version: '1.0.0',
        }),
        expect.arrayContaining([
          { Version: '1.0.0', Repo: 'stable' },
          { Version: '1.1.0', Repo: 'stable' },
        ])
      );
    });
  });

  test('should not execute the upgrade if modal is cancelled', async () => {
    const mockUpdateRelease = vi.fn();
    vi.mocked(openUpgradeHelmModal).mockResolvedValueOnce(undefined);

    const user = userEvent.setup();
    renderButton({ updateRelease: mockUpdateRelease });

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

    await waitFor(() => {
      expect(openUpgradeHelmModal).toHaveBeenCalled();
    });

    expect(mockUpdateRelease).not.toHaveBeenCalled();
  });
});
