import {Fragment} from 'react';
import styled from '@emotion/styled';

import Access from 'sentry/components/acl/access';
import {Button} from 'sentry/components/button';
import CircleIndicator from 'sentry/components/circleIndicator';
import DeprecatedAsyncComponent from 'sentry/components/deprecatedAsyncComponent';
import SentryAppIcon from 'sentry/components/sentryAppIcon';
import Tag from 'sentry/components/tag';
import {IconFlag} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {IntegrationFeature, Organization, SentryApp} from 'sentry/types';
import {toPermissions} from 'sentry/utils/consolidatedScopes';
import {
  getIntegrationFeatureGate,
  trackIntegrationAnalytics,
} from 'sentry/utils/integrationUtil';
import marked, {singleLineRenderer} from 'sentry/utils/marked';
import {recordInteraction} from 'sentry/utils/recordSentryAppInteraction';

type Props = {
  closeModal: () => void;
  isInstalled: boolean;
  onInstall: () => Promise<void>;
  organization: Organization;
  sentryApp: SentryApp;
} & DeprecatedAsyncComponent['props'];

type State = {
  featureData: IntegrationFeature[];
} & DeprecatedAsyncComponent['state'];

// No longer a modal anymore but yea :)
export default class SentryAppDetailsModal extends DeprecatedAsyncComponent<
  Props,
  State
> {
  componentDidUpdate(prevProps: Props) {
    // if the user changes org, count this as a fresh event to track
    if (this.props.organization.id !== prevProps.organization.id) {
      this.trackOpened();
    }
  }

  componentDidMount() {
    super.componentDidMount();
    this.trackOpened();
  }

  trackOpened() {
    const {sentryApp, organization, isInstalled} = this.props;
    recordInteraction(sentryApp.slug, 'sentry_app_viewed');

    trackIntegrationAnalytics(
      'integrations.install_modal_opened',
      {
        integration_type: 'sentry_app',
        integration: sentryApp.slug,
        already_installed: isInstalled,
        view: 'external_install',
        integration_status: sentryApp.status,
        organization,
      },
      {startSession: true}
    );
  }

  getEndpoints(): ReturnType<DeprecatedAsyncComponent['getEndpoints']> {
    const {sentryApp} = this.props;
    return [['featureData', `/sentry-apps/${sentryApp.slug}/features/`]];
  }

  featureTags(features: Pick<IntegrationFeature, 'featureGate'>[]) {
    return features.map(feature => {
      const feat = feature.featureGate.replace(/integrations/g, '');
      return <StyledTag key={feat}>{feat.replace(/-/g, ' ')}</StyledTag>;
    });
  }

  get permissions() {
    return toPermissions(this.props.sentryApp.scopes);
  }

  async onInstall() {
    const {onInstall} = this.props;
    // we want to make sure install finishes before we close the modal
    // and we should close the modal if there is an error as well
    try {
      await onInstall();
    } catch (_err) {
      /* stylelint-disable-next-line no-empty-block */
    }
  }

  renderPermissions() {
    const permissions = this.permissions;
    if (
      Object.keys(permissions).filter(scope => permissions[scope].length > 0).length === 0
    ) {
      return null;
    }

    return (
      <Fragment>
        <Title>Permissions</Title>
        {permissions.read.length > 0 && (
          <Permission>
            <Indicator />
            <Text key="read">
              {tct('[read] access to [resources] resources', {
                read: <strong>Read</strong>,
                resources: permissions.read.join(', '),
              })}
            </Text>
          </Permission>
        )}
        {permissions.write.length > 0 && (
          <Permission>
            <Indicator />
            <Text key="write">
              {tct('[read] and [write] access to [resources] resources', {
                read: <strong>Read</strong>,
                write: <strong>Write</strong>,
                resources: permissions.write.join(', '),
              })}
            </Text>
          </Permission>
        )}
        {permissions.admin.length > 0 && (
          <Permission>
            <Indicator />
            <Text key="admin">
              {tct('[admin] access to [resources] resources', {
                admin: <strong>Admin</strong>,
                resources: permissions.admin.join(', '),
              })}
            </Text>
          </Permission>
        )}
      </Fragment>
    );
  }

  renderBody() {
    const {sentryApp, closeModal, isInstalled, organization} = this.props;
    const {featureData} = this.state;
    // Prepare the features list
    const features = (featureData || []).map(f => ({
      featureGate: f.featureGate,
      description: (
        <span dangerouslySetInnerHTML={{__html: singleLineRenderer(f.description)}} />
      ),
    }));

    const {FeatureList, IntegrationFeatures} = getIntegrationFeatureGate();

    const overview = sentryApp.overview || '';
    const featureProps = {organization, features};

    return (
      <Fragment>
        <Heading>
          <SentryAppIcon sentryApp={sentryApp} size={50} />
          <HeadingInfo>
            <Name>{sentryApp.name}</Name>
            {!!features.length && <Features>{this.featureTags(features)}</Features>}
          </HeadingInfo>
        </Heading>
        <Description dangerouslySetInnerHTML={{__html: marked(overview)}} />
        <FeatureList {...featureProps} provider={{...sentryApp, key: sentryApp.slug}} />
        <IntegrationFeatures {...featureProps}>
          {({disabled, disabledReason}) => (
            <Fragment>
              {!disabled && this.renderPermissions()}
              <Footer>
                <Author>{t('Authored By %s', sentryApp.author)}</Author>
                <div>
                  {disabled && <DisabledNotice reason={disabledReason} />}
                  <Button size="sm" onClick={closeModal}>
                    {t('Cancel')}
                  </Button>

                  <Access access={['org:integrations']} organization={organization}>
                    {({hasAccess}) =>
                      hasAccess && (
                        <Button
                          size="sm"
                          priority="primary"
                          disabled={isInstalled || disabled}
                          onClick={() => this.onInstall()}
                          style={{marginLeft: space(1)}}
                          data-test-id="install"
                        >
                          {t('Accept & Install')}
                        </Button>
                      )
                    }
                  </Access>
                </div>
              </Footer>
            </Fragment>
          )}
        </IntegrationFeatures>
      </Fragment>
    );
  }
}

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

const HeadingInfo = styled('div')`
  display: grid;
  grid-template-rows: max-content max-content;
  align-items: start;
`;

const Name = styled('div')`
  font-weight: bold;
  font-size: 1.4em;
`;

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

  li {
    margin-bottom: 6px;
  }
`;

const Author = styled('div')`
  color: ${p => p.theme.gray300};
`;

const DisabledNotice = styled(({reason, ...p}: {reason: React.ReactNode}) => (
  <div {...p}>
    <IconFlag color="errorText" size="md" />
    {reason}
  </div>
))`
  display: grid;
  align-items: center;
  flex: 1;
  grid-template-columns: max-content 1fr;
  color: ${p => p.theme.errorText};
  font-size: 0.9em;
`;

const Text = styled('p')`
  margin: 0px 6px;
`;

const Permission = styled('div')`
  display: flex;
`;

const Footer = styled('div')`
  display: flex;
  padding: 20px 30px;
  border-top: 1px solid #e2dee6;
  margin: 20px -30px -30px;
  justify-content: space-between;
`;

const Title = styled('p')`
  margin-bottom: ${space(1)};
  font-weight: bold;
`;

const Indicator = styled(p => <CircleIndicator size={7} {...p} />)`
  margin-top: 7px;
  color: ${p => p.theme.success};
`;

const Features = styled('div')`
  margin: -${space(0.5)};
`;

const StyledTag = styled(Tag)`
  padding: ${space(0.5)};
`;
