import React, {
  ComponentPropsWithoutRef,
  ComponentRef,
  forwardRef,
} from "react"
import { tv, VariantProps } from "tailwind-variants"
import { useIsHydrated } from "../../hooks"
import { classNames } from "../../utils/classNames"
import { Avatar, AvatarImageProps } from "../Avatar"
import { Flex, FlexProps } from "../Flex"

// You only need to change this if scale or translate-x is adjusted on the left / right cards
// This is calculated by:
// 1. Removing border radius
// 2. Setting size to something large like 400px
// 3. Nudging the margin in devtools until the container is aligned to the left side of the leftmost card (e.g. -256px)
// 4. Dividing that margin by 400 (the new size)
const NEGATIVE_MARGIN_MULTIPLIER = 0.64

const animationBaseClassNames = classNames(
  "transition duration-1000 ease-out-quint",
)

const avatarVariants = tv({
  base: classNames("size-[140px] rounded-[3%]", animationBaseClassNames),
  variants: {
    show: {
      true: "",
      false: "",
    },
    position: {
      center: "*:rounded-[3%]",
      left: "translate-x-[60%] rotate-[-15deg]",
      right: "translate-x-[-60%] rotate-[15deg]",
    },
  },
  compoundVariants: [
    {
      position: ["left", "right"],
      className: "-z-10 translate-y-[4.5%] scale-75 overflow-hidden delay-150",
    },
    {
      position: "center",
      show: true,
      className: "drop-shadow-sm",
    },
    {
      position: "center",
      show: false,
      className: "translate-y-[4.5%] scale-95",
    },
    {
      position: ["left", "right"],
      show: false,
      // 12.6% (bottom aligned)
      className: "translate-y-[12.6%] rotate-0",
    },
    {
      position: "left",
      show: false,
      className: "translate-x-full",
    },
    {
      position: "right",
      show: false,
      className: "-translate-x-full",
    },
  ],
})

type Position = Exclude<
  VariantProps<typeof avatarVariants>["position"],
  undefined
>
type PositionOverrides<T> = Partial<Record<Position, T>>

type AnimationOverrides = {
  className?: string
}

type Overrides<T> = {
  base?: Partial<T>
  position?: PositionOverrides<Partial<T>>
}

export type ImageStackProps = {
  srcs?: AvatarImageProps["src"][]
  size?: AvatarImageProps["size"]

  show?: boolean
  animate?: "fade-in"

  overrides?: {
    Avatar?: Overrides<AvatarImageProps>
    animation?: Overrides<AnimationOverrides>
    centerBackground?: FlexProps
  }
} & Omit<ComponentPropsWithoutRef<typeof Flex>, "children">

export const ImageStack = forwardRef<
  ComponentRef<typeof Flex>,
  ImageStackProps
>(function ImageStack(
  {
    srcs,
    size = 140,
    show: propsShow = true,
    overrides,
    className,
    animate,
    ...rest
  },
  ref,
) {
  const isHydrated = useIsHydrated()
  const [centerSrc, rightSrc, leftSrc] = srcs || []

  const negativeMargin = size * NEGATIVE_MARGIN_MULTIPLIER * -1

  const avatarOverrides = (position: Position) => ({
    ...overrides?.Avatar?.base,
    ...overrides?.Avatar?.position?.[position],
  })

  const animationClassNames = (position: Position) =>
    classNames(
      overrides?.animation?.base?.className,
      overrides?.animation?.position?.[position]?.className,
    )

  const avatarClassNames = (position: Position) =>
    classNames(
      overrides?.Avatar?.base?.className,
      overrides?.Avatar?.position?.[position]?.className,
      animationClassNames(position),
    )

  const animationPresets: Record<
    Exclude<ImageStackProps["animate"], undefined>,
    boolean
  > = {
    "fade-in": isHydrated,
  }

  const show =
    typeof animate !== "undefined" ? animationPresets[animate] : propsShow

  return (
    <Flex
      {...rest}
      className={classNames("pointer-events-none select-none", className)}
      ref={ref}
      style={{
        marginLeft: leftSrc && negativeMargin,
        marginRight: rightSrc && negativeMargin,
        ...rest.style,
      }}
    >
      {leftSrc && (
        <Avatar
          size={size}
          src={leftSrc}
          {...avatarOverrides("left")}
          className={classNames(
            avatarVariants({
              position: "left",
              show,
            }),
            avatarClassNames("left"),
          )}
        />
      )}
      {centerSrc && (
        <Flex
          className={classNames(
            avatarVariants({
              position: "center",
              show,
            }),
            "relative opacity-100",
            avatarClassNames("center"),
          )}
          style={{ width: size, height: size }}
        >
          <Flex
            {...overrides?.centerBackground}
            className={classNames(
              "absolute inset-0 z-0 bg-bg-app",
              overrides?.centerBackground?.className,
            )}
          />
          <Avatar
            size={size}
            src={centerSrc}
            {...avatarOverrides("center")}
            className={classNames(
              "absolute inset-0 z-10",
              animationBaseClassNames,
              !show && "opacity-0",
              avatarClassNames("center"),
            )}
          />
        </Flex>
      )}
      {rightSrc && (
        <Avatar
          size={size}
          src={rightSrc}
          {...avatarOverrides("right")}
          className={classNames(
            avatarVariants({
              position: "right",
              show,
            }),
            avatarClassNames("right"),
          )}
        />
      )}
    </Flex>
  )
})
