import { memo, useState } from 'react';

import type { ListItemProps } from '~/src/components/ListItem';
import type { MappedUserPage } from '~/src/lib/songwhipApi/users/types';
import type { SelectedAnalytics } from '~/src/store/dashboard/selectors/analytics';

import ActionCard from '~/src/components/ActionCard';
import Box from '~/src/components/Box';
import PrimaryButton from '~/src/components/Button/PrimaryButton';
import FadeOnMount from '~/src/components/FadeOnMount';
import ImageIcon from '~/src/components/Icon/ImageIcon';
import ListItem from '~/src/components/ListItem';
import SearchTextInput from '~/src/components/SearchTextInput';
import Tag from '~/src/components/Tag';
import Text from '~/src/components/Text';
import useTheme from '~/src/hooks/useTheme';
import { useI18n } from '~/src/lib/i18n';
import formatDate from '~/src/lib/utils/formatDate';
import { concat } from '~/src/lib/utils/string';
import toShortNumber from '~/src/lib/utils/toShortNumber';
import TimelineHero from './TimelineHero';
import useFetchDashboardData from './useFetchDashboardData';

const SECTION_MARGIN = '4em';
const IMAGE_SIZE = '4rem';

const DashboardPages = () => {
  const { t } = useI18n('dashboard');

  const [searchValue, setSearchValue] = useState('');
  const { isLoading, items, analytics, analyticsError, refetchAnalytics } =
    useFetchDashboardData();

  const filterItems = (itemsToFilter) =>
    (itemsToFilter ?? []).filter(searchTermMatches(searchValue));

  if (!items.total && !isLoading) {
    return <NoContent />;
  }

  return (
    <Box style={{ fontSize: '1.5rem' }} padding="5rem 0 20rem">
      <TimelineHero
        key="timelineHome"
        isLoading={isLoading}
        analytics={analytics}
        error={analyticsError}
        onRetry={refetchAnalytics}
        renderDefaultText={({ totalDays }) =>
          t('visitsToAllPagesPastDays', { totalDays })
        }
      />
      <Box maxWidth="54rem" padding="2rem" isCentered>
        <DashboardSearchBox
          onUpdate={({ value }) => setSearchValue(value)}
          onClear={() => setSearchValue('')}
        />
        <DashboardListItems
          analytics={analytics}
          artists={filterItems(items.artists)}
          albums={filterItems(items.albums)}
          tracks={filterItems(items.tracks)}
          customPages={filterItems(items.customPages)}
          prereleases={filterItems(items.prereleases)}
          searchTerm={searchValue}
        />
      </Box>
    </Box>
  );
};

const NoContent = () => {
  const { t } = useI18n('dashboard');

  return (
    <Box padding="2rem" coverParent centerContent>
      <FadeOnMount>
        <ActionCard
          testId="dashboardEmpty"
          title={t('noClaimedPages')}
          description={t('noClaimedPagesDescription')}
          renderButton={() => (
            <PrimaryButton
              margin="2rem 0 0"
              text={t('labels.makeMusicLink')}
              href="/create"
            />
          )}
        />
      </FadeOnMount>
    </Box>
  );
};

const SectionTitle = ({ text }: { text: string }) => {
  const theme = useTheme();

  return (
    <Text tag="h2" margin="0 0 3rem" color={theme.textColor30} isBold>
      {text}
    </Text>
  );
};

const DashboardSearchBox = ({
  onUpdate,
  onClear,
}: {
  onUpdate: ({ value }: { value: string }) => void;
  onClear: () => void;
}) => {
  const { t } = useI18n('dashboard');

  return (
    <Box margin={`${SECTION_MARGIN} 0 0`}>
      <SearchTextInput
        testId="dashboardSearch"
        height="5rem"
        placeholder={t('searchInputPlaceholder')}
        onInputEnd={onUpdate}
        onClear={onClear}
        withClearButton
        autoFocus
      />
    </Box>
  );
};

const DashboardListItems = memo(
  ({
    analytics,
    artists,
    albums,
    tracks,
    customPages,
    prereleases,
    searchTerm,
  }: {
    analytics: SelectedAnalytics | undefined;
    artists: MappedUserPage[] | undefined;
    albums: MappedUserPage[] | undefined;
    tracks: MappedUserPage[] | undefined;
    customPages: MappedUserPage[] | undefined;
    prereleases: MappedUserPage[] | undefined;
    searchTerm: string;
  }) => {
    const { t } = useI18n('dashboard');
    const theme = useTheme();

    const noResults =
      !!searchTerm.length &&
      !artists?.length &&
      !albums?.length &&
      !tracks?.length &&
      !customPages?.length &&
      !prereleases?.length;

    return (
      <FadeOnMount>
        <div>
          {!!artists?.length && (
            <Box testId="artistsSection" margin={`${SECTION_MARGIN} 0 0`}>
              <SectionTitle text={t('labels.artistPages')} />
              <ul>
                {artists.map((artist, index) => {
                  const totalSessions =
                    analytics?.totalByItemId.artist[artist.id];

                  return (
                    <DashboardListItem
                      isFirst={!index}
                      key={artist.id}
                      title={artist.name}
                      href={`/dashboard/artist/${artist.id}`}
                      image={artist.image || undefined}
                      totalSessions={totalSessions}
                      testId="artistListItem"
                    />
                  );
                })}
              </ul>
            </Box>
          )}
          {!!prereleases?.length && (
            <Box testId="prereleasesSection" margin={`${SECTION_MARGIN} 0 0`}>
              <SectionTitle text={t('labels.prereleases')} />
              <ul>
                {prereleases.map((prerelease, index) => {
                  const totalSessions =
                    analytics?.totalByItemId[prerelease.type][prerelease.id];

                  let subtitle = prerelease.artistName;

                  const identifier =
                    prerelease.upc ?? prerelease.isrc ?? prerelease.url;

                  if (prerelease.releaseDate) {
                    subtitle = concat([
                      subtitle,
                      formatDate(prerelease.releaseDate, { useUtc: true }),
                    ]);
                  }

                  if (identifier) {
                    subtitle = concat([subtitle, identifier]);
                  }

                  return (
                    <DashboardListItem
                      isFirst={!index}
                      key={prerelease.id}
                      title={prerelease.name}
                      subtitle={subtitle}
                      href={`/dashboard/${prerelease.type}/${prerelease.id}`}
                      image={prerelease.image || undefined}
                      totalSessions={totalSessions}
                      testId="prereleaseListItem"
                    />
                  );
                })}
              </ul>
            </Box>
          )}
          {!!albums?.length && (
            <Box testId="albumsSection" margin={`${SECTION_MARGIN} 0 0`}>
              <SectionTitle text={t('labels.albumPages')} />

              <ul>
                {albums.map(
                  ({ id, name, artistName, image, isDraft, upc }, index) => {
                    const totalSessions = analytics?.totalByItemId.album[id];

                    let subtitle = artistName;

                    if (upc) {
                      subtitle = concat([subtitle, upc]);
                    }

                    const tags: string[] = [];

                    if (isDraft) tags.push('Unpublished');

                    return (
                      <DashboardListItem
                        isFirst={!index}
                        key={id}
                        title={
                          <Box flexBox alignCenter>
                            <Text
                              size="1em"
                              color={theme.textColor90}
                              weight="bold"
                              letterSpacing={0.03}
                              lineHeight="1.2em"
                              withEllipsis
                            >
                              {name}{' '}
                            </Text>
                            {!!tags?.length && (
                              <Tag
                                testId="dashboardItemTag prereleaseTag"
                                text={tags}
                                margin="0 0 0 .7rem"
                                size=".85rem"
                              />
                            )}
                          </Box>
                        }
                        subtitle={subtitle}
                        href={`/dashboard/album/${id}`}
                        image={image || undefined}
                        totalSessions={totalSessions}
                        testId="albumListItem"
                      />
                    );
                  }
                )}
              </ul>
            </Box>
          )}
          {!!tracks?.length && (
            <Box testId="tracksSection" margin={`${SECTION_MARGIN} 0 0`}>
              <SectionTitle text={t('labels.songPages')} />
              <ul>
                {tracks.map((track, index) => {
                  const totalSessions =
                    analytics?.totalByItemId.track[track.id];

                  let subtitle = track.artistName;

                  if (track.isrc) {
                    subtitle = concat([subtitle, track.isrc]);
                  }

                  return (
                    <DashboardListItem
                      isFirst={!index}
                      key={track.id}
                      title={track.name}
                      subtitle={subtitle}
                      href={`/dashboard/song/${track.id}`}
                      image={track.image || undefined}
                      totalSessions={totalSessions}
                      testId="trackListItem"
                    />
                  );
                })}
              </ul>
            </Box>
          )}
          {!!customPages?.length && (
            <Box testId="customPagesSection" margin={`${SECTION_MARGIN} 0 0`}>
              <SectionTitle text={t('labels.customPages')} />
              <ul>
                {customPages.map((customPage, index) => {
                  const totalSessions =
                    analytics?.totalByItemId.customPage[customPage.id];

                  return (
                    <DashboardListItem
                      isFirst={!index}
                      key={customPage.id}
                      title={customPage.name}
                      subtitle={customPage.artistName}
                      href={`/dashboard/custom/${customPage.id}`}
                      image={customPage.image || undefined}
                      totalSessions={totalSessions}
                      testId="customPageListItem"
                    />
                  );
                })}
              </ul>
            </Box>
          )}
          {noResults && (
            <Box testId="dashboardSearchNoResults" margin="4rem 0 0">
              <Text color={theme.textColor40} size="1.2em">
                {t('searchNotFound', { searchTerm })}
              </Text>
            </Box>
          )}
        </div>
      </FadeOnMount>
    );
  }
);

// PERF: memo() stops needless re-renders when props don't change,
// saves seconds in render time for long lists!
const DashboardListItem = memo(
  ({
    totalSessions,
    isFirst,
    ...params
  }: ListItemProps & {
    totalSessions?: number;
    isFirst: boolean;
  }) => (
    <ListItem
      {...params}
      imageSize={IMAGE_SIZE}
      fontSize="1.15em"
      spaceBetweenRows="0.2em"
      margin={!isFirst ? '2em 0 0' : undefined}
      FallbackImageIcon={ImageIcon}
      renderAfter={() =>
        totalSessions !== undefined ? (
          <VisitsDelta value={totalSessions} />
        ) : undefined
      }
    />
  )
);

const VisitsDelta = ({ value = 0, valuePrev = 0 }) => {
  const { t } = useI18n('dashboard');
  const theme = useTheme();

  const delta = value - valuePrev;
  const deltaPercent = delta / valuePrev;
  const shouldShowDelta = deltaPercent && deltaPercent !== Infinity;
  const isMore = delta > 0;

  const deltaText = shouldShowDelta
    ? ` ${isMore ? '+' : ''}${Math.round(deltaPercent * 100)}%`
    : '';

  return (
    <Text color={theme.textColor40} size="0.9em">
      {t('visitsDelta', { count: toShortNumber(value), delta: deltaText })}
    </Text>
  );
};

const searchTermMatches = (searchValue: string) => (item: MappedUserPage) => {
  const value = searchValue.toLowerCase();

  return (
    item.name.toLowerCase().includes(value) ||
    item.artistName?.toLowerCase().includes(value)
  );
};

export default DashboardPages;
