import {Fragment} from 'react';
import type {RouteComponentProps} from 'react-router';
import styled from '@emotion/styled';
import startCase from 'lodash/startCase';

import Access from 'sentry/components/acl/access';
import type {AlertProps} from 'sentry/components/alert';
import {Alert} from 'sentry/components/alert';
import DeprecatedAsyncComponent from 'sentry/components/deprecatedAsyncComponent';
import EmptyMessage from 'sentry/components/emptyMessage';
import ExternalLink from 'sentry/components/links/externalLink';
import Panel from 'sentry/components/panels/panel';
import Tag from 'sentry/components/tag';
import {Tooltip} from 'sentry/components/tooltip';
import {IconClose, IconDocs, IconGeneric, IconGithub, IconProject} from 'sentry/icons';
import {t} from 'sentry/locale';
import PluginIcon from 'sentry/plugins/components/pluginIcon';
import {space} from 'sentry/styles/space';
import type {
  IntegrationFeature,
  IntegrationInstallationStatus,
  IntegrationType,
  Organization,
} from 'sentry/types';
import type {
  IntegrationAnalyticsKey,
  IntegrationEventParameters,
} from 'sentry/utils/analytics/integrations';
import {
  getCategories,
  getIntegrationFeatureGate,
  trackIntegrationAnalytics,
} from 'sentry/utils/integrationUtil';
import marked, {singleLineRenderer} from 'sentry/utils/marked';
import BreadcrumbTitle from 'sentry/views/settings/components/settingsBreadcrumb/breadcrumbTitle';

import RequestIntegrationButton from './integrationRequest/RequestIntegrationButton';
import IntegrationStatus from './integrationStatus';

export type Tab = 'overview' | 'configurations' | 'features';

interface AlertType extends AlertProps {
  text: string;
}

type State = {
  tab: Tab;
} & DeprecatedAsyncComponent['state'];

type Props = {
  organization: Organization;
} & RouteComponentProps<{integrationSlug: string}, {}> &
  DeprecatedAsyncComponent['props'];

class AbstractIntegrationDetailedView<
  P extends Props = Props,
  S extends State = State,
> extends DeprecatedAsyncComponent<P, S> {
  tabs: Tab[] = ['overview', 'configurations'];

  componentDidMount() {
    super.componentDidMount();
    const {location} = this.props;
    const value = location.query.tab === 'configurations' ? 'configurations' : 'overview';
    // eslint-disable-next-line react/no-did-mount-set-state
    this.setState({tab: value});
  }

  onLoadAllEndpointsSuccess() {
    this.trackIntegrationAnalytics('integrations.integration_viewed', {
      integration_tab: this.state.tab,
    });
  }

  /**
   * Abstract methods defined below
   */

  // The analytics type used in analytics which is snake case
  get integrationType(): IntegrationType {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  get description(): string {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  get author(): string | undefined {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  get alerts(): AlertType[] {
    // default is no alerts
    return [];
  }

  // Returns a list of the resources displayed at the bottom of the overview card
  get resourceLinks(): Array<{title: string; url: string}> {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  get installationStatus(): IntegrationInstallationStatus | null {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  get integrationName(): string {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  // Checks to see if integration requires admin access to install, doc integrations don't
  get requiresAccess(): boolean {
    // default is integration requires access to install
    return true;
  }

  // Returns an array of RawIntegrationFeatures which is used in feature gating
  // and displaying what the integration does
  get featureData(): IntegrationFeature[] {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  getIcon(title: string) {
    switch (title) {
      case 'View Source':
        return <IconProject />;
      case 'Report Issue':
        return <IconGithub />;
      case 'Documentation':
      case 'Splunk Setup Instructions':
      case 'Trello Setup Instructions':
        return <IconDocs />;
      default:
        return <IconGeneric />;
    }
  }

  onTabChange = (value: Tab) => {
    this.trackIntegrationAnalytics('integrations.integration_tab_clicked', {
      integration_tab: value,
    });
    this.setState({tab: value});
  };

  // Returns the string that is shown as the title of a tab
  getTabDisplay(tab: Tab): string {
    // default is return the tab
    return tab;
  }

  // Render the button at the top which is usually just an installation button
  renderTopButton(
    _disabledFromFeatures: boolean, // from the feature gate
    _userHasAccess: boolean // from user permissions
  ): React.ReactElement {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  // Returns the permission descriptions, only use by Sentry Apps
  renderPermissions(): React.ReactElement | null {
    // default is don't render permissions
    return null;
  }

  renderEmptyConfigurations() {
    return (
      <Panel>
        <EmptyMessage
          title={t("You haven't set anything up yet")}
          description={t(
            'But that doesn’t have to be the case for long! Add an installation to get started.'
          )}
          action={this.renderAddInstallButton(true)}
        />
      </Panel>
    );
  }

  // Returns the list of configurations for the integration
  renderConfigurations() {
    // Allow children to implement this
    throw new Error('Not implemented');
  }

  /**
   * Actually implemented methods below
   */

  get integrationSlug() {
    return this.props.params.integrationSlug;
  }

  // Wrapper around trackIntegrationAnalytics that automatically provides many fields and the org
  trackIntegrationAnalytics = <T extends IntegrationAnalyticsKey>(
    eventKey: IntegrationAnalyticsKey,
    options?: Partial<IntegrationEventParameters[T]>
  ) => {
    options = options || {};
    // If we use this intermediate type we get type checking on the things we care about
    trackIntegrationAnalytics(eventKey, {
      view: 'integrations_directory_integration_detail',
      integration: this.integrationSlug,
      integration_type: this.integrationType,
      already_installed: this.installationStatus !== 'Not Installed', // pending counts as installed here
      organization: this.props.organization,
      ...options,
    });
  };

  // Returns the props as needed by the hooks integrations:feature-gates
  get featureProps() {
    const {organization} = this.props;
    const featureData = this.featureData;

    // Prepare the features list
    const features = featureData.map(f => ({
      featureGate: f.featureGate,
      description: (
        <FeatureListItem
          dangerouslySetInnerHTML={{__html: singleLineRenderer(f.description)}}
        />
      ),
    }));

    return {organization, features};
  }

  cleanTags() {
    return getCategories(this.featureData);
  }

  renderAlert(): React.ReactNode {
    return null;
  }

  renderAdditionalCTA(): React.ReactNode {
    return null;
  }

  renderIntegrationIcon() {
    return <PluginIcon pluginId={this.integrationSlug} size={50} />;
  }

  renderRequestIntegrationButton() {
    return (
      <RequestIntegrationButton
        organization={this.props.organization}
        name={this.integrationName}
        slug={this.integrationSlug}
        type={this.integrationType}
      />
    );
  }

  renderAddInstallButton(hideButtonIfDisabled = false) {
    const {IntegrationFeatures} = getIntegrationFeatureGate();

    return (
      <IntegrationFeatures {...this.featureProps}>
        {({disabled, disabledReason}) => (
          <DisableWrapper>
            <Access access={['org:integrations']}>
              {({hasAccess}) => (
                <Tooltip
                  title={t(
                    'You must be an organization owner, manager or admin to install this.'
                  )}
                  disabled={hasAccess || !this.requiresAccess}
                >
                  {!hideButtonIfDisabled && disabled ? (
                    <div />
                  ) : (
                    this.renderTopButton(disabled, hasAccess)
                  )}
                </Tooltip>
              )}
            </Access>
            {disabled && <DisabledNotice reason={disabledReason} />}
          </DisableWrapper>
        )}
      </IntegrationFeatures>
    );
  }

  // Returns the content shown in the top section of the integration detail
  renderTopSection() {
    const tags = this.cleanTags();

    return (
      <TopSectionWrapper>
        <Flex>
          {this.renderIntegrationIcon()}
          <NameContainer>
            <Flex>
              <Name>{this.integrationName}</Name>
              <StatusWrapper>
                {this.installationStatus && (
                  <IntegrationStatus status={this.installationStatus} />
                )}
              </StatusWrapper>
            </Flex>
            <Flex>
              {tags.map(feature => (
                <StyledTag key={feature}>{startCase(feature)}</StyledTag>
              ))}
            </Flex>
          </NameContainer>
        </Flex>
        <Flex>
          {this.renderAddInstallButton()}
          {this.renderAdditionalCTA()}
        </Flex>
      </TopSectionWrapper>
    );
  }

  // Returns the tabs divider with the clickable tabs
  renderTabs() {
    // TODO: Convert to styled component
    return (
      <ul className="nav nav-tabs border-bottom" style={{paddingTop: '30px'}}>
        {this.tabs.map(tabName => (
          <li
            key={tabName}
            className={this.state.tab === tabName ? 'active' : ''}
            onClick={() => this.onTabChange(tabName)}
          >
            <CapitalizedLink>{this.getTabDisplay(tabName)}</CapitalizedLink>
          </li>
        ))}
      </ul>
    );
  }

  // Returns the information about the integration description and features
  renderInformationCard() {
    const {FeatureList} = getIntegrationFeatureGate();

    return (
      <Fragment>
        <Flex>
          <FlexContainer>
            <Description dangerouslySetInnerHTML={{__html: marked(this.description)}} />
            <FeatureList
              {...this.featureProps}
              provider={{key: this.props.params.integrationSlug}}
            />
            {this.renderPermissions()}
            {this.alerts.map((alert, i) => (
              <Alert key={i} type={alert.type} showIcon>
                <span
                  dangerouslySetInnerHTML={{__html: singleLineRenderer(alert.text)}}
                />
              </Alert>
            ))}
          </FlexContainer>
          <Metadata>
            {!!this.author && (
              <AuthorInfo>
                <CreatedContainer>{t('Created By')}</CreatedContainer>
                <div>{this.author}</div>
              </AuthorInfo>
            )}
            {this.resourceLinks.map(({title, url}) => (
              <ExternalLinkContainer key={url}>
                {this.getIcon(title)}
                <ExternalLink href={url}>{title}</ExternalLink>
              </ExternalLinkContainer>
            ))}
          </Metadata>
        </Flex>
      </Fragment>
    );
  }

  renderBody() {
    return (
      <Fragment>
        <BreadcrumbTitle routes={this.props.routes} title={this.integrationName} />
        {this.renderAlert()}
        {this.renderTopSection()}
        {this.renderTabs()}
        {this.state.tab === 'overview'
          ? this.renderInformationCard()
          : this.renderConfigurations()}
      </Fragment>
    );
  }
}

const Flex = styled('div')`
  display: flex;
  align-items: center;
`;

const FlexContainer = styled('div')`
  flex: 1;
`;

const CapitalizedLink = styled('a')`
  text-transform: capitalize;
`;

const StyledTag = styled(Tag)`
  text-transform: none;
  &:not(:first-child) {
    margin-left: ${space(0.5)};
  }
`;

const NameContainer = styled('div')`
  display: flex;
  align-items: flex-start;
  flex-direction: column;
  justify-content: center;
  padding-left: ${space(2)};
`;

const Name = styled('div')`
  font-weight: bold;
  font-size: 1.4em;
  margin-bottom: ${space(0.5)};
`;

const IconCloseCircle = styled(IconClose)`
  color: ${p => p.theme.dangerText};
  margin-right: ${space(1)};
`;

export const DisabledNotice = styled(({reason, ...p}: {reason: React.ReactNode}) => (
  <div
    style={{
      display: 'flex',
      alignItems: 'center',
    }}
    {...p}
  >
    <IconCloseCircle isCircled />
    <span>{reason}</span>
  </div>
))`
  padding-top: ${space(0.5)};
  font-size: 0.9em;
`;

const FeatureListItem = styled('span')`
  line-height: 24px;
`;

const Description = styled('div')`
  li {
    margin-bottom: 6px;
  }
`;

const Metadata = styled(Flex)`
  display: grid;
  grid-auto-rows: max-content;
  grid-auto-flow: row;
  gap: ${space(1)};
  font-size: 0.9em;
  margin-left: ${space(4)};
  margin-right: 100px;
  align-self: flex-start;
`;

const AuthorInfo = styled('div')`
  margin-bottom: ${space(3)};
`;

const ExternalLinkContainer = styled('div')`
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: ${space(1)};
  align-items: center;
`;

const StatusWrapper = styled('div')`
  margin-bottom: ${space(0.5)};
  padding-left: ${space(2)};
`;

const DisableWrapper = styled('div')`
  margin-left: auto;
  align-self: center;
  display: flex;
  flex-direction: column;
  align-items: center;
`;

const CreatedContainer = styled('div')`
  text-transform: uppercase;
  padding-bottom: ${space(1)};
  color: ${p => p.theme.gray300};
  font-weight: 600;
  font-size: 12px;
`;

const TopSectionWrapper = styled('div')`
  display: flex;
  justify-content: space-between;
`;

export default AbstractIntegrationDetailedView;