import {useCallback, useState} from 'react';
import styled from '@emotion/styled';

import {
  addErrorMessage,
  addLoadingMessage,
  addSuccessMessage,
} from 'sentry/actionCreators/indicator';
import FieldGroup from 'sentry/components/forms/fieldGroup';
import TextField from 'sentry/components/forms/fields/textField';
import Form from 'sentry/components/forms/form';
import ExternalLink from 'sentry/components/links/externalLink';
import Panel from 'sentry/components/panels/panel';
import PanelBody from 'sentry/components/panels/panelBody';
import PanelHeader from 'sentry/components/panels/panelHeader';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import {t, tct} from 'sentry/locale';
import type {Organization, OrgAuthToken} from 'sentry/types';
import {browserHistory} from 'sentry/utils/browserHistory';
import getDynamicText from 'sentry/utils/getDynamicText';
import {handleXhrErrorResponse} from 'sentry/utils/handleXhrErrorResponse';
import {useMutation, useQueryClient} from 'sentry/utils/queryClient';
import type RequestError from 'sentry/utils/requestError/requestError';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import useApi from 'sentry/utils/useApi';
import withOrganization from 'sentry/utils/withOrganization';
import NewTokenHandler from 'sentry/views/settings/components/newTokenHandler';
import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
import TextBlock from 'sentry/views/settings/components/text/textBlock';
import {makeFetchOrgAuthTokensForOrgQueryKey} from 'sentry/views/settings/organizationAuthTokens';

type CreateTokenQueryVariables = {
  name: string;
};

type OrgAuthTokenWithToken = OrgAuthToken & {token: string};

type CreateOrgAuthTokensResponse = OrgAuthTokenWithToken;

function AuthTokenCreateForm({
  organization,
  onCreatedToken,
}: {
  onCreatedToken: (token: OrgAuthTokenWithToken) => void;
  organization: Organization;
}) {
  const initialData = {
    name: '',
  };

  const api = useApi();
  const queryClient = useQueryClient();

  const handleGoBack = useCallback(() => {
    browserHistory.push(normalizeUrl(`/settings/${organization.slug}/auth-tokens/`));
  }, [organization.slug]);

  const {mutate: submitToken} = useMutation<
    CreateOrgAuthTokensResponse,
    RequestError,
    CreateTokenQueryVariables
  >({
    mutationFn: ({name}) => {
      addLoadingMessage();
      return api.requestPromise(`/organizations/${organization.slug}/org-auth-tokens/`, {
        method: 'POST',
        data: {
          name,
        },
      });
    },

    onSuccess: (token: OrgAuthTokenWithToken) => {
      addSuccessMessage(t('Created auth token.'));

      queryClient.invalidateQueries({
        queryKey: makeFetchOrgAuthTokensForOrgQueryKey({orgSlug: organization.slug}),
      });

      onCreatedToken(token);
    },
    onError: error => {
      const detail = error.responseJSON?.detail;
      const code = detail && typeof detail === 'object' ? detail.code : undefined;

      const message =
        code === 'missing_system_url_prefix'
          ? t(
              'You have to configure `system.url-prefix` in your Sentry instance in order to generate tokens.'
            )
          : t('Failed to create a new auth token.');
      handleXhrErrorResponse(message, error);
      addErrorMessage(message);
    },
  });

  return (
    <Form
      apiMethod="POST"
      initialData={initialData}
      apiEndpoint={`/organizations/${organization.slug}/org-auth-tokens/`}
      onSubmit={({name}) => {
        submitToken({
          name,
        });
      }}
      onCancel={handleGoBack}
      submitLabel={t('Create Auth Token')}
      requireChanges
    >
      <TextField
        name="name"
        label={t('Name')}
        required
        help={t('A name to help you identify this token.')}
      />

      <FieldGroup
        label={t('Scopes')}
        help={t('Organization auth tokens currently have a limited set of scopes.')}
      >
        <div>
          <div>org:ci</div>
          <ScopeHelpText>{t('Source Map Upload, Release Creation')}</ScopeHelpText>
        </div>
      </FieldGroup>
    </Form>
  );
}

export function OrganizationAuthTokensNewAuthToken({
  organization,
}: {
  organization: Organization;
}) {
  const [newToken, setNewToken] = useState<OrgAuthTokenWithToken | null>(null);

  const handleGoBack = useCallback(() => {
    browserHistory.push(normalizeUrl(`/settings/${organization.slug}/auth-tokens/`));
  }, [organization.slug]);

  return (
    <div>
      <SentryDocumentTitle title={t('Create New Auth Token')} />
      <SettingsPageHeader title={t('Create New Auth Token')} />

      <TextBlock>
        {t(
          'Organization Auth Tokens can be used in many places to interact with Sentry programatically. For example, they can be used for sentry-cli, bundler plugins or similar uses cases.'
        )}
      </TextBlock>
      <TextBlock>
        {tct(
          'For more information on how to use the web API, see our [link:documentation].',
          {
            link: <ExternalLink href="https://docs.sentry.io/api/" />,
          }
        )}
      </TextBlock>
      <Panel>
        <PanelHeader>{t('Create New Auth Token')}</PanelHeader>

        <PanelBody>
          {newToken ? (
            <NewTokenHandler
              token={getDynamicText({value: newToken.token, fixed: 'ORG_AUTH_TOKEN'})}
              handleGoBack={handleGoBack}
            />
          ) : (
            <AuthTokenCreateForm
              organization={organization}
              onCreatedToken={setNewToken}
            />
          )}
        </PanelBody>
      </Panel>
    </div>
  );
}

export default withOrganization(OrganizationAuthTokensNewAuthToken);

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