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

import {useRole} from 'sentry/components/acl/useRole';
import Tag from 'sentry/components/badge/tag';
import {LinkButton} from 'sentry/components/button';
import FileSize from 'sentry/components/fileSize';
import Link from 'sentry/components/links/link';
import Pagination from 'sentry/components/pagination';
import Panel from 'sentry/components/panels/panel';
import {PanelTable} from 'sentry/components/panels/panelTable';
import SearchBar from 'sentry/components/searchBar';
import TimeSince from 'sentry/components/timeSince';
import {Tooltip} from 'sentry/components/tooltip';
import {IconClock, IconDownload} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
import type {Project} from 'sentry/types/project';
import type {Artifact} from 'sentry/types/release';
import type {DebugIdBundleArtifact} from 'sentry/types/sourceMaps';
import {keepPreviousData, useApiQuery} from 'sentry/utils/queryClient';
import {decodeScalar} from 'sentry/utils/queryString';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import useApi from 'sentry/utils/useApi';
import useOrganization from 'sentry/utils/useOrganization';
import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
import {DebugIdBundleDeleteButton} from 'sentry/views/settings/projectSourceMaps/debugIdBundleDeleteButton';
import {DebugIdBundleDetails} from 'sentry/views/settings/projectSourceMaps/debugIdBundleDetails';
import {useDeleteDebugIdBundle} from 'sentry/views/settings/projectSourceMaps/useDeleteDebugIdBundle';

enum DebugIdBundleArtifactType {
  INVALID = 0,
  SOURCE = 1,
  MINIFIED_SOURCE = 2,
  SOURCE_MAP = 3,
  INDEXED_RAM_BUNDLE = 4,
}

const debugIdBundleTypeLabels = {
  [DebugIdBundleArtifactType.INVALID]: t('Invalid'),
  [DebugIdBundleArtifactType.SOURCE]: t('Source'),
  [DebugIdBundleArtifactType.MINIFIED_SOURCE]: t('Minified'),
  [DebugIdBundleArtifactType.SOURCE_MAP]: t('Source Map'),
  [DebugIdBundleArtifactType.INDEXED_RAM_BUNDLE]: t('Indexed RAM Bundle'),
};

function ArtifactsTableRow({
  name,
  downloadUrl,
  size,
  type,
  orgSlug,
  artifactColumnDetails,
}: {
  artifactColumnDetails: React.ReactNode;
  downloadUrl: string;
  name: string;
  orgSlug: string;
  size: number;
  type?: string;
}) {
  const {hasRole, roleRequired: downloadRole} = useRole({role: 'debugFilesRole'});

  return (
    <Fragment>
      <ArtifactColumn>
        <Name>{name || `(${t('empty')})`}</Name>
        {artifactColumnDetails}
      </ArtifactColumn>
      {type && <TypeColumn>{type}</TypeColumn>}
      <SizeColumn>
        <FileSize bytes={size} />
      </SizeColumn>
      <ActionsColumn>
        <Tooltip
          title={tct(
            'Artifacts can only be downloaded by users with organization [downloadRole] role[orHigher]. This can be changed in [settingsLink:Debug Files Access] settings.',
            {
              downloadRole,
              orHigher: downloadRole !== 'owner' ? ` ${t('or higher')}` : '',
              settingsLink: <Link to={`/settings/${orgSlug}/#debugFilesRole`} />,
            }
          )}
          disabled={hasRole}
          isHoverable
        >
          <LinkButton
            size="sm"
            icon={<IconDownload size="sm" />}
            disabled={!hasRole}
            href={downloadUrl}
            title={hasRole ? t('Download Artifact') : undefined}
            aria-label={t('Download Artifact')}
          />
        </Tooltip>
      </ActionsColumn>
    </Fragment>
  );
}

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

export function SourceMapsDetails({params, location, router, project}: Props) {
  const api = useApi();
  const organization = useOrganization();

  // query params
  const query = decodeScalar(location.query.query);
  const cursor = location.query.cursor ?? '';

  // endpoints
  const artifactsEndpoint = `/projects/${organization.slug}/${
    project.slug
  }/releases/${encodeURIComponent(params.bundleId)}/files/`;
  const debugIdBundlesArtifactsEndpoint = `/projects/${organization.slug}/${
    project.slug
  }/artifact-bundles/${encodeURIComponent(params.bundleId)}/files/`;

  // debug id bundles tab url
  const debugIdsUrl = normalizeUrl(
    `/settings/${organization.slug}/projects/${project.slug}/source-maps/${params.bundleId}/`
  );

  const tabDebugIdBundlesActive = location.pathname === debugIdsUrl;

  const {
    data: artifactsData,
    getResponseHeader: artifactsHeaders,
    isPending: artifactsLoading,
  } = useApiQuery<Artifact[]>(
    [
      artifactsEndpoint,
      {
        query: {query, cursor},
      },
    ],
    {
      staleTime: 0,
      placeholderData: keepPreviousData,
      enabled: !tabDebugIdBundlesActive,
    }
  );

  const {
    data: debugIdBundlesArtifactsData,
    getResponseHeader: debugIdBundlesArtifactsHeaders,
    isPending: debugIdBundlesArtifactsLoading,
  } = useApiQuery<DebugIdBundleArtifact>(
    [
      debugIdBundlesArtifactsEndpoint,
      {
        query: {query, cursor},
      },
    ],
    {
      staleTime: 0,
      placeholderData: keepPreviousData,
      enabled: tabDebugIdBundlesActive,
    }
  );

  const {mutate: deleteDebugIdArtifacts} = useDeleteDebugIdBundle({
    onSuccess: () =>
      router.push(`/settings/${organization.slug}/projects/${project.slug}/source-maps/`),
  });

  const handleDeleteDebugIdBundle = useCallback(() => {
    if (!debugIdBundlesArtifactsData) {
      return;
    }
    deleteDebugIdArtifacts({
      projectSlug: project.slug,
      bundleId: debugIdBundlesArtifactsData.bundleId,
    });
  }, [debugIdBundlesArtifactsData, deleteDebugIdArtifacts, project.slug]);

  const handleSearch = useCallback(
    (newQuery: string) => {
      router.push({
        ...location,
        query: {...location.query, cursor: undefined, query: newQuery},
      });
    },
    [router, location]
  );

  return (
    <Fragment>
      <SettingsPageHeader
        title={tabDebugIdBundlesActive ? params.bundleId : t('Release Bundle')}
        action={
          tabDebugIdBundlesActive && (
            <DebugIdBundleDeleteButton size="sm" onDelete={handleDeleteDebugIdBundle} />
          )
        }
        subtitle={
          !tabDebugIdBundlesActive && (
            <VersionAndDetails>{params.bundleId}</VersionAndDetails>
          )
        }
      />
      {tabDebugIdBundlesActive && debugIdBundlesArtifactsData && (
        <DetailsPanel>
          <DebugIdBundleDetails debugIdBundle={debugIdBundlesArtifactsData} />
        </DetailsPanel>
      )}
      <SearchBarWithMarginBottom
        placeholder={
          tabDebugIdBundlesActive ? t('Filter by Path or ID') : t('Filter by Path')
        }
        onSearch={handleSearch}
        query={query}
      />
      <StyledPanelTable
        hasTypeColumn={tabDebugIdBundlesActive}
        headers={[
          t('Artifact'),
          ...(tabDebugIdBundlesActive
            ? [<TypeColumn key="type">{t('Type')}</TypeColumn>]
            : []),
          <SizeColumn key="file-size">{t('File Size')}</SizeColumn>,
          '',
        ]}
        emptyMessage={
          query
            ? t('No artifacts match your search query.')
            : t('There are no artifacts in this upload.')
        }
        isEmpty={
          (tabDebugIdBundlesActive
            ? debugIdBundlesArtifactsData?.files ?? []
            : artifactsData ?? []
          ).length === 0
        }
        isLoading={
          tabDebugIdBundlesActive ? debugIdBundlesArtifactsLoading : artifactsLoading
        }
      >
        {tabDebugIdBundlesActive
          ? (debugIdBundlesArtifactsData?.files ?? []).map(data => {
              const downloadUrl = `${api.baseUrl}/projects/${organization.slug}/${
                project.slug
              }/artifact-bundles/${encodeURIComponent(params.bundleId)}/files/${
                data.id
              }/?download=1`;

              return (
                <ArtifactsTableRow
                  key={data.id}
                  size={data.fileSize}
                  name={data.filePath}
                  type={
                    debugIdBundleTypeLabels[data.fileType as DebugIdBundleArtifactType]
                  }
                  downloadUrl={downloadUrl}
                  orgSlug={organization.slug}
                  artifactColumnDetails={
                    <Fragment>
                      {data.sourcemap ? (
                        <div>
                          <SubText>{t('Sourcemap Reference:')}</SubText> {data.sourcemap}
                        </div>
                      ) : null}
                      {data.debugId ? (
                        <div>
                          <SubText>{t('Debug ID:')}</SubText> {data.debugId}
                        </div>
                      ) : null}
                    </Fragment>
                  }
                />
              );
            })
          : artifactsData?.map(data => {
              const downloadUrl = `${api.baseUrl}/projects/${organization.slug}/${
                project.slug
              }/releases/${encodeURIComponent(params.bundleId)}/files/${
                data.id
              }/?download=1`;

              return (
                <ArtifactsTableRow
                  key={data.id}
                  size={data.size}
                  name={data.name}
                  downloadUrl={downloadUrl}
                  orgSlug={organization.slug}
                  artifactColumnDetails={
                    <TimeAndDistWrapper>
                      <TimeWrapper>
                        <IconClock size="sm" />
                        <TimeSince date={data.dateCreated} />
                      </TimeWrapper>
                      <StyledTag
                        type={data.dist ? 'info' : undefined}
                        tooltipText={data.dist ? undefined : t('No distribution set')}
                      >
                        {data.dist ?? t('none')}
                      </StyledTag>
                    </TimeAndDistWrapper>
                  }
                />
              );
            })}
      </StyledPanelTable>
      <Pagination
        pageLinks={
          tabDebugIdBundlesActive
            ? debugIdBundlesArtifactsHeaders?.('Link') ?? ''
            : artifactsHeaders?.('Link') ?? ''
        }
      />
    </Fragment>
  );
}

const StyledPanelTable = styled(PanelTable)<{hasTypeColumn: boolean}>`
  grid-template-columns: minmax(220px, 1fr) minmax(120px, max-content) minmax(
      74px,
      max-content
    );
  ${p =>
    p.hasTypeColumn &&
    `
  grid-template-columns:
    minmax(220px, 1fr) minmax(120px, max-content) minmax(120px, max-content)
    minmax(74px, max-content);
    `}
`;

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

const ActionsColumn = styled(Column)`
  justify-content: flex-end;
`;

const SearchBarWithMarginBottom = styled(SearchBar)`
  margin-bottom: ${space(3)};
`;

const DetailsPanel = styled(Panel)`
  padding: ${space(1)} ${space(2)};
`;

const ArtifactColumn = styled('div')`
  overflow-wrap: break-word;
  word-break: break-all;
  line-height: 140%;
  display: flex;
  flex-direction: column;
  justify-content: center;
`;

const Name = styled('div')`
  display: flex;
  justify-content: flex-start;
  align-items: center;
`;

const TypeColumn = styled('div')`
  display: flex;
  justify-content: flex-end;
  text-align: right;
  align-items: center;
  color: ${p => p.theme.subText};
`;

const SizeColumn = styled('div')`
  display: flex;
  justify-content: flex-end;
  text-align: right;
  align-items: center;
  color: ${p => p.theme.subText};
`;

const TimeAndDistWrapper = styled('div')`
  width: 100%;
  display: flex;
  margin-top: ${space(1)};
  align-items: center;
`;

const TimeWrapper = styled('div')`
  display: grid;
  gap: ${space(0.5)};
  grid-template-columns: min-content 1fr;
  font-size: ${p => p.theme.fontSizeMedium};
  align-items: center;
  color: ${p => p.theme.subText};
`;

const StyledTag = styled(Tag)`
  margin-left: ${space(1)};
`;

const SubText = styled('span')`
  color: ${p => p.theme.subText};
`;

const VersionAndDetails = styled('div')`
  display: flex;
  flex-direction: column;
  gap: ${space(1)};
  word-break: break-word;
`;