import classnames from 'classnames/bind';
import React, { Children, ReactNode, useCallback, useState } from 'react';
import { KeyCode } from '../../helpers/key-code';
import { BoxDivProps, SilkeBox } from '../silke-box';
import { SilkeButton, SilkeButtonSet, SilkeButtonSetProps } from '../silke-button';
import ButtonContext from '../silke-button/context';
import { SilkeIcon, SilkeIcons } from '../silke-icon';
import { SilkeImage } from '../silke-image';
import { SilkeOverflowMenu, SilkeOverflowMenuItem } from '../silke-overflow-menu';
import { SilkeSkeleton } from '../silke-skeleton';
import { SilkeTagGreen } from '../silke-tag';
import { SilkeText, SilkeTitle } from '../silke-text';
import styles from './silke-tile.scss';

const cl = classnames.bind(styles);

export type SilkeTileProps = {
  image?: string;
  icon?: SilkeIcons;
  autoFocus?: boolean;
  title?: React.ReactNode;
  titleKind?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  subTitle?: React.ReactNode;
  text?: string;
  tags?: string[];
  loading?: boolean;
  /**
   * Width of tile
   * tiny = 256
   * small = 512
   * medium = 768
   * large = 1024
   */
  width?: 'tiny' | 'small' | 'medium' | 'large';
  selected?: boolean;
  menu?: SilkeOverflowMenuItem[];
  expandable?: boolean | (() => Promise<void>);
  expanded?: boolean;
  onExpand?: (expanding?: boolean) => void | Promise<void>;
  expandOpenText?: string;
  expandCloseText?: string;
  expandLoadingText?: string;
  hideSelectedCheck?: boolean;
  children?: React.ReactNode | React.ReactNode[];
} & Omit<BoxDivProps, 'title' | 'width' | 'height' | 'size' | 'children'>;

export function SilkeTile({
  image,
  icon,
  title,
  titleKind = 'h5',
  subTitle,
  text,
  loading,
  children,
  className,
  width,
  selected,
  menu,
  expanded,
  expandable,
  onExpand,
  tags,
  expandOpenText = 'Show more',
  expandCloseText = 'Hide',
  expandLoadingText = 'Loading...',
  hideSelectedCheck = false,
  ...rest
}: SilkeTileProps) {
  const [loadingExpand, setLoadingExpand] = useState(false);
  const [toggle, setToggle] = useState(!!expanded);

  let actions: ReactNode = null;
  const filteredChildren = Children.map(children, (c) => {
    if (React.isValidElement(c) && c.type === SilkeTileActions) {
      actions = c;
      return false;
    }

    return c;
  });
  const handleToggle = useCallback(async () => {
    setToggle(!toggle);

    // Async expand
    if (onExpand && !toggle) {
      const res = onExpand(true);
      if (res) {
        setLoadingExpand(true);
        await res;
        setLoadingExpand(false);
      }
    }
  }, [toggle, onExpand]);

  return (
    <SilkeBox
      column
      rounded="medium"
      role={rest.onClick ? 'link' : undefined}
      tabIndex={rest.onClick ? 0 : undefined}
      onKeyUp={
        typeof rest.onClick === 'function'
          ? (e: any) => {
              if (e.key === KeyCode.enter && rest.onClick) {
                rest.onClick(e);
              }
            }
          : undefined
      }
      className={cl('tile', className, width, {
        clickable: Boolean(rest.onClick),
        selected,
      })}
      {...rest}
    >
      {loading && (
        <SilkeSkeleton style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />
      )}
      {image && <SilkeImage aspectRatio="16:9" src={image} />}
      <SilkeBox pad="l" gap="m" flex>
        <SilkeBox gap="xs" column flex>
          {(icon || title || subTitle) && (
            <SilkeBox vAlign="center" gap="s">
              {icon && <SilkeImage icon={icon} avatar="m" />}

              <SilkeBox gap="xs" column flex>
                <SilkeBox>
                  <SilkeTitle kind="xs" flex overflow tag={titleKind || 'h5'}>
                    {title}
                  </SilkeTitle>
                  {expandable && (
                    <SilkeButton
                      loading={loadingExpand}
                      kind="ghost"
                      label={
                        loadingExpand
                          ? expandLoadingText
                          : toggle
                            ? expandCloseText
                            : expandOpenText
                      }
                      icon={toggle ? 'up' : 'down'}
                      size="base"
                      onClick={handleToggle}
                    />
                  )}
                  {menu && <SilkeOverflowMenu items={menu} kind="ghost" size="base" />}
                </SilkeBox>

                {subTitle && <SilkeText overflow>{subTitle}</SilkeText>}
              </SilkeBox>

              {tags && (
                <SilkeBox className={styles.tags}>
                  {tags.map((t, i) => (
                    <SilkeTagGreen key={i} label={t} />
                  ))}
                </SilkeBox>
              )}
            </SilkeBox>
          )}

          {expandable ? (
            <div>
              {toggle && (
                <ButtonContext kind="secondary">
                  {loadingExpand ? <SilkeSkeleton kind="multiline" /> : filteredChildren}
                </ButtonContext>
              )}
            </div>
          ) : (
            <ButtonContext kind="secondary">{filteredChildren}</ButtonContext>
          )}
        </SilkeBox>
        {selected && !hideSelectedCheck && (
          <SilkeBox column vAlign="spread" hAlign="center" width="m">
            <SilkeText color="accent-50">
              <SilkeIcon icon="check.circle" />
            </SilkeText>
          </SilkeBox>
        )}
      </SilkeBox>
      {actions}
    </SilkeBox>
  );
}

type SilkeTileActions = Omit<SilkeButtonSetProps, 'size'>;

export function SilkeTileActions({ children, className, ...rest }: SilkeTileActions) {
  const hasAtLeastTwoButtons = React.Children.toArray(children).filter(Boolean).length > 1;

  return hasAtLeastTwoButtons ? (
    <SilkeButtonSet gap="s" hAlign="end" pad="m" kind="primary" className={className} {...rest}>
      {children}
    </SilkeButtonSet>
  ) : (
    <SilkeBox pad="m">{children}</SilkeBox>
  );
}
