import type { SongwhipEvent } from '@theorchard/songwhip-events';
import type { ReactNode } from 'react';
import type { ClickableProps } from '../Clickable';
import type { Icon } from '../Icon/toIcon';
import type { ImageProps } from '../Image';
import type { TextProps } from '../Text';

import useTheme from '~/src/hooks/useTheme';
import toSizedImageUrlNext from '~/src/lib/toSizedImageUrlNext';
import Box from '../Box';
import Clickable from '../Clickable';
import ChevronIcon from '../Icon/ChevronIcon';
import Image from '../Image';
import Text from '../Text';

export interface ListItemProps<TData = unknown>
  extends Omit<ClickableProps<TData>, 'title' | 'tag' | 'children'> {
  title: ReactNode;
  subtitle?: string;
  withTitleEllipsis?: boolean;
  withSubtitleEllipsis?: boolean;
  height?: number | string;
  image?: string;
  imageSize?: number | string;
  FallbackImageIcon?: Icon;
  imageProps?: ImageProps;
  SongwhipEvent?: SongwhipEvent;
  isDisabled?: boolean;
  testId?: string;
  withDivider?: boolean;
  fontSize?: number | string;
  margin?: string;
  padding?: string;
  tag?: string;
  spaceBetweenRows?: number | string;
  titleWeight?: TextProps['weight'];
  renderBefore?: () => ReactNode;
  renderAfter?: () => ReactNode;
  renderAfterOuter?: () => ReactNode;
  squareImage?: boolean;
  hideChevron?: boolean;
  header?: ReactNode;
  lineHeight?: string;
  subtitleSize?: string;
}

const DEFAULT_IMAGE_SIZE = '5rem';
const DEFAULT_FONT_SIZE = '1.8rem';

const ListItem = <TData,>(props: ListItemProps<TData>) => {
  const {
    height,
    margin,
    title,
    subtitle,
    imageSize = DEFAULT_IMAGE_SIZE,
    withTitleEllipsis = true,
    withSubtitleEllipsis = true,
    titleWeight = 'bold' as const,
    testId = 'listItem',
    withDivider,
    fontSize = DEFAULT_FONT_SIZE,
    spaceBetweenRows = 1,
    imageProps,
    padding,
    renderBefore,
    tag = 'li',
    renderAfter,
    renderAfterOuter,
    hideChevron,
    image,
    squareImage,
    header,
    lineHeight,
    subtitleSize,
    FallbackImageIcon,
    ...clickableProps
  } = props;

  const theme = useTheme();
  const imageDefined = 'image' in props;
  const isClickable = !!(clickableProps.href || clickableProps.onClick);

  const sizedImage =
    image &&
    toSizedImageUrlNext({
      url: image,
      // always output 75px image from image service
      width: 50 * 1.5,
      // webp can be slow to resize and this shows when loading 10s of list items
      format: 'jpeg',
    });

  const imagePropsInternal = {
    margin: `0 1.5rem 0 0`,
    width: imageSize,
    height: imageSize,
    style: {
      borderRadius: squareImage ? '.3rem' : '50%',
      border: `solid 1px ${theme.textColor50}`,
    },
  };

  return (
    <Box
      tag={tag}
      flexRow
      alignCenter
      margin={margin}
      padding={padding}
      className="listItem"
      data-testid={testId}
      style={{
        height,
        borderTop: withDivider ? `solid 1px ${theme.textColor10}` : undefined,
        color: theme.textColor,
      }}
    >
      {renderBefore && renderBefore()}

      <ItemContent {...clickableProps}>
        <Box padding="0" flexRow flexGrow alignCenter>
          {imageDefined && sizedImage ? (
            <Image
              alt=""
              {...imagePropsInternal}
              // url may be undefined but we still want to render a placeholder
              src={sizedImage}
              // always render frame as 1:1 aspect
              aspect={1}
              isLazy
              {...imageProps}
            />
          ) : FallbackImageIcon ? (
            <Box {...imagePropsInternal} centerContent>
              <FallbackImageIcon
                color={theme.textColor30}
                size="2rem"
                testId="fallbackImageIcon"
              />
            </Box>
          ) : null}
          <Text flexGrow alignCenter flexColumn size={fontSize}>
            {header && (
              <Text
                size="0.7em"
                lineHeight={lineHeight ?? '1.2em'}
                color={theme.textColor50}
                margin={`0 0 ${spaceBetweenRows}`}
                letterSpacing={0.03}
                textTransform="capitalize"
              >
                {header}
              </Text>
            )}
            <Text
              testId={`${testId.split(' ')[0]}Title`}
              size="1em"
              color={theme.textColor90}
              withEllipsis={withTitleEllipsis}
              weight={titleWeight}
              letterSpacing={0.03}
              lineHeight={lineHeight ?? '1.2em'}
              title={
                withTitleEllipsis && typeof title === 'string'
                  ? title
                  : undefined
              }
            >
              {title}
            </Text>
            {subtitle && (
              <Text
                testId={`${testId.split(' ')[0]}Subtitle`}
                size={subtitleSize ?? '0.8em'}
                lineHeight={lineHeight ?? '1.25em'}
                color={theme.textColor50}
                margin={`${spaceBetweenRows} 0 0`}
                withEllipsis={withSubtitleEllipsis}
                letterSpacing={0.03}
                title={
                  withSubtitleEllipsis && typeof subtitle === 'string'
                    ? subtitle
                    : undefined
                }
              >
                {subtitle}
              </Text>
            )}
          </Text>
        </Box>

        {renderAfter?.()}
        {!hideChevron && isClickable && !renderAfter && (
          <Box className="chevron">
            <ChevronIcon
              direction="right"
              size="2rem"
              opacity={0.4}
              margin="0 -0.3rem 0 0.5rem"
            />
          </Box>
        )}
      </ItemContent>
      {renderAfterOuter?.()}

      <style jsx global>{`
        .listItem:hover .chevron {
          transform: translateX(5%);
        }

        .listItem:hover .chevron > svg {
          opacity: 0.6 !important;
        }
      `}</style>
    </Box>
  );
};

const ItemContent = ({ href, onClick, children, ...rest }: ClickableProps) => {
  if (href || onClick)
    return (
      <Clickable
        {...rest}
        href={href}
        onClick={onClick}
        flexBox
        flexGrow
        alignCenter
      >
        {children}
      </Clickable>
    );

  return (
    <Box flexBox flexGrow>
      {children}
    </Box>
  );
};

export default ListItem;
