import "./releases.scss";

import React, { Component } from "react";
import kebabCase from "lodash/kebabCase";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router";
import { autobind, interval } from "../../utils";
import { releaseStore } from "./release.store";
import { helmChartStore } from "../+apps-helm-charts/helm-chart.store";
import { IReleaseRouteParams, releaseURL } from "./release.route";
import { HelmRelease } from "../../api/endpoints/helm-releases.api";
import { ReleaseDetails } from "./release-details";
import { ReleaseRollbackDialog } from "./release-rollback-dialog";
import { navigation } from "../../navigation";
import { ItemListLayout } from "../item-object-list/item-list-layout";
import { HelmReleaseMenu } from "./release-menu";
import { Icon } from "../icon";
import { secretsStore } from "../+config-secrets/secrets.store";
import { when } from "mobx";

enum sortBy {
  name = "name",
  namespace = "namespace",
  revision = "revision",
  chart = "chart",
  status = "status",
  updated = "update"
}

interface Props extends RouteComponentProps<IReleaseRouteParams> {
}

@observer
export class HelmReleases extends Component<Props> {
  private versionsWatcher = interval(3600, this.checkVersions);

  componentDidMount() {
    // Watch for secrets associated with releases and react to their changes
    releaseStore.watch();
    this.versionsWatcher.start();
    when(() => releaseStore.isLoaded, this.checkVersions);
  }

  componentWillUnmount() {
    releaseStore.unwatch();
    this.versionsWatcher.stop();
  }

  // Check all available versions every 1 hour for installed releases.
  // This required to show "upgrade" icon in the list and upgrade button in the details view.
  @autobind()
  checkVersions() {
    const charts = releaseStore.items.map(release => release.getChart());
    return charts.reduce((promise, chartName) => {
      const loadVersions = () => helmChartStore.getVersions(chartName, true);
      return promise.then(loadVersions, loadVersions);
    }, Promise.resolve({}))
  };

  get selectedRelease() {
    const { match: { params: { name, namespace } } } = this.props;
    return releaseStore.items.find(release => {
      return release.getName() == name && release.getNs() == namespace;
    });
  }

  showDetails = (item: HelmRelease) => {
    if (!item) {
      navigation.merge(releaseURL())
    }
    else {
      navigation.merge(releaseURL({
        params: {
          name: item.getName(),
          namespace: item.getNs()
        }
      }))
    }
  }

  hideDetails = () => {
    this.showDetails(null);
  }

  renderRemoveDialogMessage(selectedItems: HelmRelease[]) {
    const releaseNames = selectedItems.map(item => item.getName()).join(", ");
    return (
      <div>
        <Trans>Remove <b>{releaseNames}</b>?</Trans>
        <p className="warning">
          <Trans>Note: StatefulSet Volumes won't be deleted automatically</Trans>
        </p>
      </div>
    )
  }

  render() {
    return (
      <>
        <ItemListLayout
          className="HelmReleases"
          store={releaseStore}
          dependentStores={[secretsStore]}
          sortingCallbacks={{
            [sortBy.name]: (release: HelmRelease) => release.getName(),
            [sortBy.namespace]: (release: HelmRelease) => release.getNs(),
            [sortBy.revision]: (release: HelmRelease) => release.getRevision(),
            [sortBy.chart]: (release: HelmRelease) => release.getChart(),
            [sortBy.status]: (release: HelmRelease) => release.getStatus(),
            [sortBy.updated]: (release: HelmRelease) => release.getUpdated(false, false),
          }}
          searchFilters={[
            (release: HelmRelease) => release.getName(),
            (release: HelmRelease) => release.getNs(),
            (release: HelmRelease) => release.getChart(),
            (release: HelmRelease) => release.getStatus(),
            (release: HelmRelease) => release.getVersion(),
          ]}
          renderHeaderTitle={<Trans>Releases</Trans>}
          renderTableHeader={[
            { title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
            { title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
            { title: <Trans>Chart</Trans>, className: "chart", sortBy: sortBy.chart },
            { title: <Trans>Revision</Trans>, className: "revision", sortBy: sortBy.revision },
            { title: <Trans>Version</Trans>, className: "version" },
            { title: <Trans>App Version</Trans>, className: "app-version" },
            { title: <Trans>Status</Trans>, className: "status", sortBy: sortBy.status },
            { title: <Trans>Updated</Trans>, className: "updated", sortBy: sortBy.updated },
          ]}
          renderTableContents={(release: HelmRelease) => {
            const version = release.getVersion();
            const lastVersion = release.getLastVersion();
            return [
              release.getName(),
              release.getNs(),
              release.getChart(),
              release.getRevision(),
              <>
                {version}
                {!lastVersion && (
                  <Icon
                    small svg="spinner"
                    className="checking-update"
                    tooltip={<Trans>Checking update</Trans>}
                  />
                )}
                {release.hasNewVersion() && (
                  <Icon
                    material="new_releases"
                    className="new-version"
                    tooltip={<Trans>New version: {lastVersion}</Trans>}
                  />
                )}
              </>,
              release.appVersion,
              { title: release.getStatus(), className: kebabCase(release.getStatus()) },
              release.getUpdated(),
            ]
          }}
          renderItemMenu={(release: HelmRelease) => {
            return (
              <HelmReleaseMenu
                release={release}
                removeConfirmationMessage={this.renderRemoveDialogMessage([release])}
              />
            )
          }}
          customizeRemoveDialog={(selectedItems: HelmRelease[]) => ({
            message: this.renderRemoveDialogMessage(selectedItems)
          })}
          detailsItem={this.selectedRelease}
          onDetails={this.showDetails}
        />
        <ReleaseDetails
          release={this.selectedRelease}
          hideDetails={this.hideDetails}
        />
        <ReleaseRollbackDialog/>
      </>
    );
  }
}