<script>
import {
  GlAlert,
  GlTooltipDirective,
  GlCard,
  GlToggle,
  GlLink,
  GlSkeletonLoader,
  GlIcon,
  GlSafeHtmlDirective,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import Tracking from '~/tracking';
import { __, s__ } from '~/locale';
import {
  TRACK_TOGGLE_TRAINING_PROVIDER_ACTION,
  TRACK_TOGGLE_TRAINING_PROVIDER_LABEL,
  TRACK_PROVIDER_LEARN_MORE_CLICK_ACTION,
  TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL,
} from '~/security_configuration/constants';
import dismissUserCalloutMutation from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql';
import securityTrainingProvidersQuery from '~/security_configuration/graphql/security_training_providers.query.graphql';
import configureSecurityTrainingProvidersMutation from '~/security_configuration/graphql/configure_security_training_providers.mutation.graphql';
import {
  updateSecurityTrainingCache,
  updateSecurityTrainingOptimisticResponse,
} from '~/security_configuration/graphql/cache_utils';
import { TEMP_PROVIDER_LOGOS, TEMP_PROVIDER_URLS } from './constants';

const i18n = {
  providerQueryErrorMessage: __(
    'Could not fetch training providers. Please refresh the page, or try again later.',
  ),
  configMutationErrorMessage: __(
    'Could not save configuration. Please refresh the page, or try again later.',
  ),
  primaryTraining: s__('SecurityTraining|Primary Training'),
  primaryTrainingDescription: s__(
    'SecurityTraining|Training from this partner takes precedence when more than one training partner is enabled.',
  ),
};

export default {
  components: {
    GlAlert,
    GlCard,
    GlToggle,
    GlLink,
    GlSkeletonLoader,
    GlIcon,
  },
  directives: {
    GlTooltip: GlTooltipDirective,
    SafeHtml: GlSafeHtmlDirective,
  },
  mixins: [Tracking.mixin()],
  inject: ['projectFullPath'],
  apollo: {
    securityTrainingProviders: {
      query: securityTrainingProvidersQuery,
      variables() {
        return {
          fullPath: this.projectFullPath,
        };
      },
      update({ project }) {
        return project?.securityTrainingProviders;
      },
      error() {
        this.errorMessage = this.$options.i18n.providerQueryErrorMessage;
      },
    },
  },
  data() {
    return {
      errorMessage: '',
      securityTrainingProviders: [],
      hasTouchedConfiguration: false,
    };
  },
  computed: {
    enabledProviders() {
      return this.securityTrainingProviders.filter(({ isEnabled }) => isEnabled);
    },
    isLoading() {
      return this.$apollo.queries.securityTrainingProviders.loading;
    },
  },
  created() {
    const unwatchConfigChance = this.$watch('hasTouchedConfiguration', () => {
      this.dismissFeaturePromotionCallout();
      unwatchConfigChance();
    });
  },
  methods: {
    async dismissFeaturePromotionCallout() {
      try {
        const {
          data: {
            userCalloutCreate: { errors },
          },
        } = await this.$apollo.mutate({
          mutation: dismissUserCalloutMutation,
          variables: {
            input: {
              featureName: 'security_training_feature_promotion',
            },
          },
        });

        // handle errors reported from the backend
        if (errors?.length > 0) {
          throw new Error(errors[0]);
        }
      } catch (e) {
        Sentry.captureException(e);
      }
    },
    async toggleProvider(provider) {
      const { isEnabled, isPrimary } = provider;
      const toggledIsEnabled = !isEnabled;

      this.trackProviderToggle(provider.id, toggledIsEnabled);

      // when the current primary provider gets disabled then set the first enabled to be the new primary
      if (!toggledIsEnabled && isPrimary && this.enabledProviders.length > 1) {
        const firstOtherEnabledProvider = this.enabledProviders.find(
          ({ id }) => id !== provider.id,
        );
        this.setPrimaryProvider(firstOtherEnabledProvider);
      }

      this.storeProvider({
        ...provider,
        isEnabled: toggledIsEnabled,
      });
    },
    setPrimaryProvider(provider) {
      this.storeProvider({ ...provider, isPrimary: true });
    },
    async storeProvider(provider) {
      const { id, isEnabled, isPrimary } = provider;
      let nextIsPrimary = isPrimary;

      // if the current provider has been disabled it can't be primary
      if (!isEnabled) {
        nextIsPrimary = false;
      }

      // if the current provider is the only enabled provider it should be primary
      if (isEnabled && !this.enabledProviders.length) {
        nextIsPrimary = true;
      }

      try {
        const {
          data: {
            securityTrainingUpdate: { errors = [] },
          },
        } = await this.$apollo.mutate({
          mutation: configureSecurityTrainingProvidersMutation,
          variables: {
            input: {
              projectPath: this.projectFullPath,
              providerId: id,
              isEnabled,
              isPrimary: nextIsPrimary,
            },
          },
          optimisticResponse: updateSecurityTrainingOptimisticResponse({
            id,
            isEnabled,
            isPrimary: nextIsPrimary,
          }),
          update: updateSecurityTrainingCache({
            query: securityTrainingProvidersQuery,
            variables: { fullPath: this.projectFullPath },
          }),
        });

        if (errors.length > 0) {
          // throwing an error here means we can handle scenarios within the `catch` block below
          throw new Error();
        }

        this.hasTouchedConfiguration = true;
      } catch {
        this.errorMessage = this.$options.i18n.configMutationErrorMessage;
      }
    },
    trackProviderToggle(providerId, providerIsEnabled) {
      this.track(TRACK_TOGGLE_TRAINING_PROVIDER_ACTION, {
        label: TRACK_TOGGLE_TRAINING_PROVIDER_LABEL,
        property: providerId,
        extra: {
          providerIsEnabled,
        },
      });
    },
    trackProviderLearnMoreClick(providerId) {
      this.track(TRACK_PROVIDER_LEARN_MORE_CLICK_ACTION, {
        label: TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL,
        property: providerId,
      });
    },
  },
  i18n,
  TEMP_PROVIDER_LOGOS,
  TEMP_PROVIDER_URLS,
};
</script>

<template>
  <div>
    <gl-alert v-if="errorMessage" variant="danger" :dismissible="false" class="gl-mb-6">
      {{ errorMessage }}
    </gl-alert>
    <div
      v-if="isLoading"
      class="gl-bg-white gl-py-6 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
    >
      <gl-skeleton-loader :width="350" :height="44">
        <rect width="200" height="8" x="10" y="0" rx="4" />
        <rect width="300" height="8" x="10" y="15" rx="4" />
        <rect width="100" height="8" x="10" y="35" rx="4" />
      </gl-skeleton-loader>
    </div>
    <ul v-else class="gl-list-style-none gl-m-0 gl-p-0">
      <li v-for="provider in securityTrainingProviders" :key="provider.id" class="gl-mb-6">
        <gl-card>
          <div class="gl-display-flex">
            <gl-toggle
              :value="provider.isEnabled"
              :label="__('Training mode')"
              label-position="hidden"
              @change="toggleProvider(provider)"
            />
            <div v-if="$options.TEMP_PROVIDER_LOGOS[provider.name]" class="gl-ml-4">
              <div
                v-safe-html="$options.TEMP_PROVIDER_LOGOS[provider.name].svg"
                data-testid="provider-logo"
                style="width: 18px"
                role="presentation"
              ></div>
            </div>
            <div class="gl-ml-3">
              <h3 class="gl-font-lg gl-m-0 gl-mb-2">{{ provider.name }}</h3>
              <p>
                {{ provider.description }}
                <gl-link
                  v-if="$options.TEMP_PROVIDER_URLS[provider.name]"
                  :href="$options.TEMP_PROVIDER_URLS[provider.name]"
                  target="_blank"
                  @click="trackProviderLearnMoreClick(provider.id)"
                >
                  {{ __('Learn more.') }}
                </gl-link>
              </p>
              <!-- Note: The following `div` and it's content will be replaced by 'GlFormRadio' once https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1720#note_857342988 is resolved -->
              <div
                class="gl-form-radio custom-control custom-radio"
                data-testid="primary-provider-radio"
              >
                <input
                  :id="`security-training-provider-${provider.id}`"
                  type="radio"
                  :checked="provider.isPrimary"
                  class="custom-control-input"
                  :disabled="!provider.isEnabled"
                  @change="setPrimaryProvider(provider)"
                />
                <label
                  class="custom-control-label"
                  :for="`security-training-provider-${provider.id}`"
                >
                  {{ $options.i18n.primaryTraining }}
                </label>
                <gl-icon
                  v-gl-tooltip="$options.i18n.primaryTrainingDescription"
                  name="information-o"
                  class="gl-ml-2 gl-cursor-help"
                />
              </div>
            </div>
          </div>
        </gl-card>
      </li>
    </ul>
  </div>
</template>
