import React from "react"
import { classNames } from "../../utils/classNames"
import { Flex } from "../Flex"
import { FlexColumn } from "../FlexColumn"
import { Skeleton } from "../Skeleton"
import { Text } from "../Text"

type ChartSkeletonBaseProps = {
  className?: string
  children?: React.ReactNode
}

export type ChartSkeletonProps = ChartSkeletonBaseProps & {
  xAxisLabels?: BaselineProps["labels"]
  yAxisLabels?: OrdinateProps["labels"]

  contentClassName?: string
  skeletonClassName?: string
}

function ChartSkeletonBase({ className, children }: ChartSkeletonBaseProps) {
  return (
    <div className={classNames("pointer-events-none relative", className)}>
      <div className="absolute inset-0">{children}</div>
    </div>
  )
}

type ChartSkeletonLayoutProps = ChartSkeletonProps & {
  content: React.ReactNode
}

function ChartSkeletonLayout({
  xAxisLabels,
  yAxisLabels,
  className,
  contentClassName,
  skeletonClassName,
  content,
  children,
}: ChartSkeletonLayoutProps) {
  const hasYAxisLabels = (yAxisLabels?.length || 0) > 0
  const hasXAxisLabels = (xAxisLabels?.length || 0) > 0

  return (
    <ChartSkeletonBase className={className}>
      {hasXAxisLabels && (
        <ChartSkeleton.Baseline
          labels={xAxisLabels || []}
          skeletonClassName={skeletonClassName}
        />
      )}
      {hasYAxisLabels && (
        <ChartSkeleton.Ordinate
          labels={yAxisLabels || []}
          skeletonClassName={skeletonClassName}
        />
      )}
      <ChartSkeleton.Content
        className={classNames(
          "relative",
          hasXAxisLabels && "h-[calc(100%-theme(spacing.8))]",
        )}
      >
        <span className={classNames("absolute inset-0", contentClassName)}>
          {content}
        </span>

        {children}
      </ChartSkeleton.Content>
    </ChartSkeletonBase>
  )
}

type ContentProps = {
  children: React.ReactNode
  className?: string
}

function Content({ children, className }: ContentProps) {
  return (
    <Flex
      className={classNames("flex-row items-end justify-around", className)}
    >
      {children}
    </Flex>
  )
}

type OrdinateProps = {
  labels: React.ReactNode[] | string[]

  className?: string
  skeletonClassName?: string
}

function Ordinate({ labels, className, skeletonClassName }: OrdinateProps) {
  return (
    <FlexColumn
      className={classNames("absolute inset-0 mb-8 justify-between", className)}
    >
      {labels.map((label, index) => (
        <Flex className="relative items-center gap-3" key={index}>
          {typeof label === "string" ? (
            <Text.Label
              className={classNames(
                "shrink-0 animate-pulse text-2xs normal-case text-text-secondary",
                skeletonClassName,
              )}
            >
              {label}
            </Text.Label>
          ) : (
            label
          )}
          <Skeleton.Line
            className={classNames(
              "absolute h-px w-full grow",
              skeletonClassName,
            )}
          />
        </Flex>
      ))}
      <div></div>
    </FlexColumn>
  )
}

type BaselineProps = {
  labels: React.ReactNode[] | string[]

  className?: string
  skeletonClassName?: string
}

function Baseline({ labels, className, skeletonClassName }: BaselineProps) {
  return (
    <Flex
      className={classNames(
        "absolute bottom-0 h-8 w-full items-end",
        className,
      )}
    >
      <FlexColumn className="w-full">
        <Skeleton.Line
          className={classNames("mt-auto h-px w-full", skeletonClassName)}
        />
        <Flex className="w-full justify-around">
          {labels.map((label, index) => (
            <FlexColumn className="items-center gap-1" key={index}>
              <Skeleton.Line
                className={classNames("h-2.5 w-px", skeletonClassName)}
              />
              {typeof label === "string" ? (
                <Text.Label
                  className={classNames(
                    "animate-pulse text-2xs normal-case text-text-secondary",
                    skeletonClassName,
                  )}
                >
                  {label}
                </Text.Label>
              ) : (
                label
              )}
            </FlexColumn>
          ))}
        </Flex>
      </FlexColumn>
    </Flex>
  )
}

export const ChartSkeleton = Object.assign(ChartSkeletonLayout, {
  Base: ChartSkeletonBase,
  Content,
  Ordinate,
  Baseline,
})
