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

import {addErrorMessage} from 'sentry/actionCreators/indicator';
import Checkbox from 'sentry/components/checkbox';
import Pagination from 'sentry/components/pagination';
import {PanelTable} from 'sentry/components/panels/panelTable';
import SearchBar from 'sentry/components/searchBar';
import {t} from 'sentry/locale';
import ProjectsStore from 'sentry/stores/projectsStore';
import {space} from 'sentry/styles/space';
import type {BuiltinSymbolSource, CustomRepo, DebugFile} from 'sentry/types/debugFiles';
import type {Organization} from 'sentry/types/organization';
import type {Project} from 'sentry/types/project';
import routeTitleGen from 'sentry/utils/routeTitle';
import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
import TextBlock from 'sentry/views/settings/components/text/textBlock';
import PermissionAlert from 'sentry/views/settings/project/permissionAlert';

import DebugFileRow from './debugFileRow';
import Sources from './sources';

type Props = RouteComponentProps<{projectId: string}, {}> & {
  organization: Organization;
  project: Project;
};

type State = DeprecatedAsyncView['state'] & {
  debugFiles: DebugFile[] | null;
  project: Project;
  showDetails: boolean;
  builtinSymbolSources?: BuiltinSymbolSource[] | null;
};

class ProjectDebugSymbols extends DeprecatedAsyncView<Props, State> {
  getTitle() {
    const {projectId} = this.props.params;

    return routeTitleGen(t('Debug Files'), projectId, false);
  }

  getDefaultState() {
    return {
      ...super.getDefaultState(),
      project: this.props.project,
      showDetails: false,
    };
  }

  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
    const {organization, params, location} = this.props;
    const {builtinSymbolSources} = this.state || {};
    const {query} = location.query;

    const endpoints: ReturnType<DeprecatedAsyncView['getEndpoints']> = [
      [
        'debugFiles',
        `/projects/${organization.slug}/${params.projectId}/files/dsyms/`,
        {
          query: {query},
        },
      ],
    ];

    if (!builtinSymbolSources && organization.features.includes('symbol-sources')) {
      endpoints.push([
        'builtinSymbolSources',
        `/organizations/${organization.slug}/builtin-symbol-sources/`,
        {},
      ]);
    }

    return endpoints;
  }

  handleDelete = (id: string) => {
    const {organization, params} = this.props;

    this.setState({
      loading: true,
    });

    this.api.request(
      `/projects/${organization.slug}/${params.projectId}/files/dsyms/?id=${id}`,
      {
        method: 'DELETE',
        complete: () => this.fetchData(),
      }
    );
  };

  handleSearch = (query: string) => {
    const {location, router} = this.props;

    router.push({
      ...location,
      query: {...location.query, cursor: undefined, query},
    });
  };

  async fetchProject() {
    const {organization, params} = this.props;
    try {
      const updatedProject = await this.api.requestPromise(
        `/projects/${organization.slug}/${params.projectId}/`
      );
      ProjectsStore.onUpdateSuccess(updatedProject);
    } catch {
      addErrorMessage(t('An error occurred while fetching project data'));
    }
  }

  getQuery() {
    const {query} = this.props.location.query;

    return typeof query === 'string' ? query : undefined;
  }

  getEmptyMessage() {
    if (this.getQuery()) {
      return t('There are no debug symbols that match your search.');
    }

    return t('There are no debug symbols for this project.');
  }

  renderLoading() {
    return this.renderBody();
  }

  renderDebugFiles() {
    const {debugFiles, showDetails} = this.state;
    const {organization, params, project} = this.props;

    if (!debugFiles?.length) {
      return null;
    }

    return debugFiles.map(debugFile => {
      const downloadUrl = `${this.api.baseUrl}/projects/${organization.slug}/${params.projectId}/files/dsyms/?id=${debugFile.id}`;

      return (
        <DebugFileRow
          debugFile={debugFile}
          showDetails={showDetails}
          downloadUrl={downloadUrl}
          downloadRole={organization.debugFilesRole}
          onDelete={this.handleDelete}
          key={debugFile.id}
          orgSlug={organization.slug}
          project={project}
        />
      );
    });
  }

  renderBody() {
    const {organization, project, router, location} = this.props;
    const {loading, showDetails, builtinSymbolSources, debugFiles, debugFilesPageLinks} =
      this.state;

    return (
      <Fragment>
        <SettingsPageHeader title={t('Debug Information Files')} />

        <TextBlock>
          {t(`
            Debug information files are used to convert addresses and minified
            function names from native crash reports into function names and
            locations.
          `)}
        </TextBlock>

        {organization.features.includes('symbol-sources') && (
          <Fragment>
            <PermissionAlert project={project} />

            <Sources
              api={this.api}
              location={location}
              router={router}
              project={project}
              organization={organization}
              customRepositories={
                (project.symbolSources
                  ? JSON.parse(project.symbolSources)
                  : []) as CustomRepo[]
              }
              builtinSymbolSources={project.builtinSymbolSources ?? []}
              builtinSymbolSourceOptions={builtinSymbolSources ?? []}
              isLoading={loading}
            />
          </Fragment>
        )}

        <Wrapper>
          <TextBlock noMargin>{t('Uploaded debug information files')}</TextBlock>
          <Filters>
            <Label>
              <Checkbox
                checked={showDetails}
                onChange={e => {
                  this.setState({showDetails: (e.target as HTMLInputElement).checked});
                }}
              />
              {t('show details')}
            </Label>

            <SearchBar
              placeholder={t('Search DIFs')}
              onSearch={this.handleSearch}
              query={this.getQuery()}
            />
          </Filters>
        </Wrapper>

        <StyledPanelTable
          headers={[
            t('Debug ID'),
            t('Information'),
            <Actions key="actions">{t('Actions')}</Actions>,
          ]}
          emptyMessage={this.getEmptyMessage()}
          isEmpty={debugFiles?.length === 0}
          isLoading={loading}
        >
          {this.renderDebugFiles()}
        </StyledPanelTable>
        <Pagination pageLinks={debugFilesPageLinks} />
      </Fragment>
    );
  }
}

const StyledPanelTable = styled(PanelTable)`
  grid-template-columns: 37% 1fr auto;
`;

const Actions = styled('div')`
  text-align: right;
`;

const Wrapper = styled('div')`
  display: grid;
  grid-template-columns: auto 1fr;
  gap: ${space(4)};
  align-items: center;
  margin-top: ${space(4)};
  margin-bottom: ${space(1)};
  @media (max-width: ${p => p.theme.breakpoints.small}) {
    display: block;
  }
`;

const Filters = styled('div')`
  display: grid;
  grid-template-columns: min-content minmax(200px, 400px);
  align-items: center;
  justify-content: flex-end;
  gap: ${space(2)};
  @media (max-width: ${p => p.theme.breakpoints.small}) {
    grid-template-columns: min-content 1fr;
  }
`;

const Label = styled('label')`
  font-weight: normal;
  display: flex;
  align-items: center;
  margin-bottom: 0;
  white-space: nowrap;
  gap: ${space(1)};
`;

export default ProjectDebugSymbols;
