import { ArrowUp } from 'lucide-react';
import { useRouter } from '@uirouter/react';
import { useState } from 'react';

import { EnvironmentId } from '@/react/portainer/environments/types';
import { notifySuccess } from '@/portainer/services/notifications';
import { semverCompare } from '@/react/common/semver-utils';

import { Button, LoadingButton } from '@@/buttons';
import { InlineLoader } from '@@/InlineLoader';
import { Tooltip } from '@@/Tip/Tooltip';
import { Link } from '@@/Link';

import { HelmRelease, UpdateHelmReleasePayload } from '../../types';
import { useUpdateHelmReleaseMutation } from '../../queries/useUpdateHelmReleaseMutation';
import { useHelmRepoVersions } from '../../queries/useHelmRepoVersions';
import { useHelmRelease } from '../queries/useHelmRelease';
import { useHelmRegistries } from '../../queries/useHelmRegistries';

import { openUpgradeHelmModal } from './UpgradeHelmModal';

export function UpgradeButton({
  environmentId,
  releaseName,
  namespace,
  release,
  updateRelease,
}: {
  environmentId: EnvironmentId;
  releaseName: string;
  namespace: string;
  release?: HelmRelease;
  updateRelease: (release: HelmRelease) => void;
}) {
  const router = useRouter();
  const [useCache, setUseCache] = useState(true);
  const updateHelmReleaseMutation = useUpdateHelmReleaseMutation(environmentId);

  const registriesQuery = useHelmRegistries();
  const helmRepoVersionsQuery = useHelmRepoVersions(
    release?.chart.metadata?.name || '',
    60 * 60 * 1000, // 1 hour
    registriesQuery.data,
    useCache
  );
  const versions = helmRepoVersionsQuery.data;
  const repo = versions?.[0]?.Repo;

  // Combined loading state
  const isLoading =
    registriesQuery.isInitialLoading || helmRepoVersionsQuery.isFetching; // use 'isFetching' for helmRepoVersionsQuery because we want to show when it's refetching
  const isError = registriesQuery.isError || helmRepoVersionsQuery.isError;
  const latestVersionQuery = useHelmRelease(
    environmentId,
    releaseName,
    namespace,
    {
      select: (data) => data.chart.metadata?.version,
    }
  );
  const latestVersionAvailable = versions[0]?.Version ?? '';
  const isNewVersionAvailable = Boolean(
    latestVersionQuery?.data &&
      semverCompare(latestVersionAvailable, latestVersionQuery?.data) === 1
  );
  const currentVersion = release?.chart.metadata?.version;

  const editableHelmRelease: UpdateHelmReleasePayload = {
    name: releaseName,
    namespace: namespace || '',
    values: release?.values?.userSuppliedValues,
    chart: release?.chart.metadata?.name || '',
    version: currentVersion,
    repo,
  };

  return (
    <div className="relative">
      <LoadingButton
        color="secondary"
        data-cy="k8sApp-upgradeHelmChartButton"
        onClick={handleUpgrade}
        disabled={
          versions.length === 0 ||
          isLoading ||
          isError ||
          release?.info?.status?.startsWith('pending')
        }
        loadingText="Upgrading..."
        isLoading={updateHelmReleaseMutation.isLoading}
        icon={ArrowUp}
        size="medium"
      >
        Upgrade
      </LoadingButton>
      {isLoading && (
        <InlineLoader
          size="xs"
          className="absolute -bottom-5 left-0 right-0 whitespace-nowrap"
        >
          Checking for new versions...
        </InlineLoader>
      )}
      {!isLoading && !isError && (
        <span className="absolute flex items-center -bottom-5 left-0 right-0 text-xs text-muted text-center whitespace-nowrap">
          {getStatusMessage(
            versions.length === 0,
            latestVersionAvailable,
            isNewVersionAvailable
          )}
          {versions.length === 0 && (
            <Tooltip
              message={
                <div>
                  Portainer is unable to find any versions for this chart in the
                  repositories saved. Try adding a new repository which contains
                  the chart in the{' '}
                  <Link
                    to="portainer.account"
                    params={{ '#': 'helm-repositories' }}
                    data-cy="user-settings-link"
                  >
                    Helm repositories settings
                  </Link>
                </div>
              }
            />
          )}
          <Button
            data-cy="k8sApp-refreshHelmChartVersionsButton"
            color="link"
            size="xsmall"
            onClick={handleRefreshVersions}
            type="button"
          >
            Refresh
          </Button>
        </span>
      )}
    </div>
  );

  function handleRefreshVersions() {
    if (useCache) {
      // clicking 'refresh versions' should get the latest versions from the repo, not the cached versions
      setUseCache(false);
    }
    helmRepoVersionsQuery.refetch();
  }

  async function handleUpgrade() {
    const submittedUpgradeValues = await openUpgradeHelmModal(
      editableHelmRelease,
      versions
    );

    if (submittedUpgradeValues) {
      upgrade(submittedUpgradeValues, release);
    }

    function upgrade(payload: UpdateHelmReleasePayload, release?: HelmRelease) {
      if (release?.info) {
        const updatedRelease = {
          ...release,
          info: {
            ...release.info,
            status: 'pending-upgrade',
            description: 'Preparing upgrade',
          },
        };
        updateRelease(updatedRelease);
      }
      updateHelmReleaseMutation.mutate(payload, {
        onSuccess: () => {
          notifySuccess('Success', 'Helm chart upgraded successfully');
          // set the revision url param to undefined to refresh the page at the latest revision
          router.stateService.go('kubernetes.helm', {
            namespace,
            name: releaseName,
            revision: undefined,
          });
        },
      });
    }
  }
}

function getStatusMessage(
  hasNoAvailableVersions: boolean,
  latestVersionAvailable: string,
  isNewVersionAvailable: boolean
) {
  if (hasNoAvailableVersions) {
    return 'No versions available ';
  }
  if (isNewVersionAvailable) {
    return `New version available (${latestVersionAvailable}) `;
  }
  return 'Latest version installed';
}
