import { render, screen, waitFor } from '@testing-library/react';
import { HttpResponse } from 'msw';

import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
import { server, http } from '@/setup-tests/server';
import { withTestRouter } from '@/react/test-utils/withRouter';
import { UserViewModel } from '@/portainer/models/user';
import { withUserProvider } from '@/react/test-utils/withUserProvider';
import { mockCodeMirror } from '@/setup-tests/mock-codemirror';
import { mockLocalizeDate } from '@/setup-tests/mock-localizeDate';

import { HelmApplicationView } from './HelmApplicationView';

// Mock the necessary hooks and dependencies
const mockUseCurrentStateAndParams = vi.fn();
const mockUseEnvironmentId = vi.fn();
mockLocalizeDate();

vi.mock('@uirouter/react', async (importOriginal: () => Promise<object>) => ({
  ...(await importOriginal()),
  useCurrentStateAndParams: () => mockUseCurrentStateAndParams(),
}));

vi.mock('@/react/hooks/useEnvironmentId', () => ({
  useEnvironmentId: () => mockUseEnvironmentId(),
}));

mockCodeMirror();

const minimalHelmRelease = {
  name: 'test-release',
  version: '1',
  namespace: 'default',
  chart: {
    metadata: {
      name: 'test-chart',
      version: '2.2.2',
    },
  },
  info: {
    status: 'deployed',
    last_deployed: '2021-01-01T00:00:00Z',
    // notes: 'This is a test note', // can be missing for a minimal release
  },
  manifest: 'This is a test manifest',
};

// Create a more complete helm release object for testing
const completeHelmRelease = {
  name: 'test-release',
  version: '1',
  namespace: 'default',
  chart: {
    metadata: {
      name: 'test-chart',
      appVersion: '1.0.0',
      version: '2.2.2',
    },
  },
  info: {
    status: 'deployed',
    notes: 'This is a test note',
    resources: [
      {
        kind: 'Deployment',
        name: 'test-deployment',
        namespace: 'default',
        uid: 'test-deployment-uid',
        status: {
          healthSummary: {
            status: 'Healthy',
            reason: 'Running',
            message: 'All replicas are ready',
          },
        },
        metadata: {
          name: 'test-deployment',
          namespace: 'default',
        },
      },
      {
        kind: 'Service',
        name: 'test-service',
        namespace: 'default',
        uid: 'test-service-uid',
        status: {
          healthSummary: {
            status: 'Healthy',
            reason: 'Available',
            message: 'Service is available',
          },
        },
        metadata: {
          name: 'test-service',
          namespace: 'default',
        },
      },
    ],
  },
  manifest: 'This is a test manifest',
  values: {
    // Add some values to ensure the Values tab is present
    replicaCount: 1,
    image: {
      repository: 'nginx',
      tag: 'latest',
    },
  },
  resources: [
    {
      kind: 'Deployment',
      name: 'test-deployment',
      namespace: 'default',
      status: {
        healthSummary: {
          status: 'Healthy',
          reason: 'Running',
          message: 'All replicas are ready',
        },
      },
      metadata: {
        name: 'test-deployment',
        namespace: 'default',
      },
    },
  ],
};

const helmReleaseHistory = [
  {
    version: 1,
    updated: '2023-06-01T12:00:00Z',
    status: 'deployed',
    chart: 'test-chart-1.0.0',
    app_version: '1.0.0',
    description: 'Install complete',
  },
];

function renderComponent() {
  const user = new UserViewModel({ Username: 'user' });
  const Wrapped = withTestQueryProvider(
    withUserProvider(withTestRouter(HelmApplicationView), user)
  );
  return render(<Wrapped />);
}

describe(
  'HelmApplicationView',
  () => {
    beforeEach(() => {
      // Set up default mock values
      mockUseEnvironmentId.mockReturnValue(3);
      mockUseCurrentStateAndParams.mockReturnValue({
        params: {
          name: 'test-release',
          namespace: 'default',
        },
      });
    });

    it('should display helm release details for minimal release when data is loaded', async () => {
      vi.spyOn(console, 'error').mockImplementation(() => {});

      server.use(
        http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
          HttpResponse.json(minimalHelmRelease)
        ),
        http.get('/api/users/undefined/helm/repositories', () =>
          HttpResponse.json({
            GlobalRepository: 'https://charts.helm.sh/stable',
            UserRepositories: [
              { Id: '1', URL: 'https://charts.helm.sh/stable' },
            ],
          })
        ),
        http.get('/api/templates/helm', () =>
          HttpResponse.json({
            entries: {
              'test-chart': [{ version: '1.0.0' }],
            },
          })
        ),
        http.get('/api/endpoints/3/kubernetes/helm/test-release/history', () =>
          HttpResponse.json(helmReleaseHistory)
        ),
        http.get('/api/kubernetes/3/namespaces/default/events', () =>
          HttpResponse.json([])
        )
      );

      const { findByText, findAllByText } = renderComponent();

      // Check for the page header
      expect(await findByText('Helm details')).toBeInTheDocument();

      // Check for the badge content
      expect(await findByText(/Namespace: default/)).toBeInTheDocument();
      expect(
        await findByText(/Chart version: test-chart-2.2.2/)
      ).toBeInTheDocument();
      expect(await findByText(/Chart: test-chart/)).toBeInTheDocument();
      expect(await findByText(/Revision: #1/)).toBeInTheDocument();
      expect(
        await findByText(/Last deployed: Jan 1, 2021, 12:00 AM/)
      ).toBeInTheDocument();
      // Check for the actual values
      expect(await findAllByText(/test-release/)).toHaveLength(2); // title and badge
      expect(await findAllByText(/test-chart/)).toHaveLength(2); // title and badge (not checking revision list item)

      // There shouldn't be a notes tab when there are no notes
      expect(screen.queryByText(/Notes/)).not.toBeInTheDocument();

      // There shouldn't be an app version badge when it's missing
      expect(screen.queryByText(/App version/)).not.toBeInTheDocument();

      // Ensure there are no console errors
      // eslint-disable-next-line no-console
      expect(console.error).not.toHaveBeenCalled();

      // Restore console.error
      vi.spyOn(console, 'error').mockRestore();
    });

    it('should display error message when API request fails', async () => {
      // Mock API failure
      server.use(
        http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
          HttpResponse.error()
        ),
        // Add mock for events endpoint
        http.get('/api/kubernetes/3/namespaces/default/events', () =>
          HttpResponse.json([])
        )
      );

      // Mock console.error to prevent test output pollution
      vi.spyOn(console, 'error').mockImplementation(() => {});

      renderComponent();

      // Wait for the error message to appear
      expect(
        await screen.findByText(
          'Failed to load Helm application details',
          {},
          { timeout: 6500 }
        )
      ).toBeInTheDocument();

      // Restore console.error
      vi.spyOn(console, 'error').mockRestore();
    });

    it('should display additional details when available in helm release', async () => {
      server.use(
        http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
          HttpResponse.json(completeHelmRelease)
        ),
        http.get('/api/endpoints/3/kubernetes/helm/test-release/history', () =>
          HttpResponse.json(helmReleaseHistory)
        ),
        http.get('/api/kubernetes/3/namespaces/default/events', () =>
          HttpResponse.json([])
        )
      );

      const { findByText } = renderComponent();

      expect(await findByText('Helm details')).toBeInTheDocument();

      // Check for the app version badge when it's available
      await waitFor(() => {
        expect(
          screen.getByText(/App version/, { exact: false })
        ).toBeInTheDocument();
      });

      await waitFor(() => {
        // Look for specific tab text
        expect(screen.getByText('Resources')).toBeInTheDocument();
        expect(screen.getByText('Values')).toBeInTheDocument();
        expect(screen.getByText('Manifest')).toBeInTheDocument();
        expect(screen.getByText('Notes')).toBeInTheDocument();
        expect(screen.getByText('Events')).toBeInTheDocument();
      });

      expect(await findByText(/App version: 1.0.0/)).toBeInTheDocument();
    });
  },
  {
    timeout: 7000,
  }
);
