import React, { useMemo, useState } from "react";
import * as twPlugins from "tailwindcss/lib/plugins";
import * as corePlugins from "tailwindcss/lib/corePlugins";
import * as twPluginList from "tailwindcss/lib/corePluginList";
import resolveConfig from "tailwindcss/resolveConfig";
import { Box } from "../components/Box";
import { Heading } from "../components/Heading";
import { SimpleTable } from "../components/SimpleTable";
import { Code } from "./Code";
import { Link } from "../components/Link";

import { tailwindConfig } from "../tokens/_tailwind-config";
import { camelToTitleCase } from "../utilities";
import { Flex } from "../components/Flex";
import { Icon, ICON_TYPE } from "../components/Icon";
import { Reference } from "../components/Reference";

const config = resolveConfig(tailwindConfig as any);

type Variant =
  | "responsive"
  | "first"
  | "last"
  | "odd"
  | "even"
  | "visited"
  | "checked"
  | "group-hover"
  | "group-focus"
  | "focus-within"
  | "hover"
  | "focus"
  | "focus-visible"
  | "active"
  | "disabled"
  | "dark";

export const getTailwindPluginList = (): string[] =>
  twPluginList.corePluginList.filter((p: string) => p !== "preflight").sort();

const useTailwindPluginList = () => {
  const [pluginList, setPluginList] = useState<string[]>([]);
  const corePluginList: string[] = twPluginList
    ? twPluginList.corePluginList
    : [];
  if (corePluginList.length - 1 !== pluginList.length) {
    setPluginList(getTailwindPluginList());
  }
  return pluginList;
};

export interface TailwindPropertyClassesProps {
  pluginName: string;
}

const PropertyRow = ({
  propertyKey,
  value,
}: {
  propertyKey: string;
  value: { [key: string]: string };
}) => {
  return (
    <SimpleTable.Row className="border-b border-gray-200">
      <SimpleTable.Cell className="break-words whitespace-pre-wrap">
        {propertyKey}
      </SimpleTable.Cell>
      <SimpleTable.Cell className="break-words whitespace-pre-wrap">
        <Flex className="flex-col items-start">
          {Object.keys(value).map((prop, i) => {
            const isLast = i === Object.keys(value).length - 1;
            return (
              <Code key={prop} className={isLast ? "" : "mb-1"}>
                {prop}: {value[prop]}
              </Code>
            );
          })}
        </Flex>
      </SimpleTable.Cell>
    </SimpleTable.Row>
  );
};

interface PropertiesState {
  [key: string]: {
    [key: string]: string;
  };
}

const getPropertiesStateArray = (state: PropertiesState): PropertiesState[] => {
  return Array.isArray(state) ? state : [state];
};

export const TailwindPropertyClasses = ({
  pluginName,
}: TailwindPropertyClassesProps) => {
  const [
    propertiesState,
    setPropertiesState,
  ] = useState<PropertiesState | null>(null);

  const propertiesStateArray = useMemo(() => {
    return propertiesState ? getPropertiesStateArray(propertiesState) : [];
  }, [propertiesState]);

  const wrapperFunc = twPlugins[pluginName];
  if (!wrapperFunc) return null;

  const addUtilities = (properties: PropertiesState) => {
    if (!propertiesState) {
      setPropertiesState(properties);
    }
  };
  const themeFunc = (key: string) => {
    const parts = key.split(".");
    return parts.reduce<any>((acc, cur) => acc[cur], config.theme);
  };
  const variantsFunc = () => {};
  const prefix = () => "";

  const pluginFunction = wrapperFunc();

  pluginFunction({
    addUtilities,
    theme: themeFunc,
    variants: variantsFunc,
    prefix,
    corePlugins: corePlugins.default,
    addComponents: addUtilities,
  });

  const headingText = camelToTitleCase(pluginName);
  const variants: Variant[] = (config.variants as any)[pluginName] || [];

  return (
    <Box className="mb-10">
      <Flex className="items-center mb-2 justify-between">
        <Heading.H2 className="inline-flex items-start" id={headingText}>
          {headingText}
        </Heading.H2>
        <Link href="#tailwind-classes" target="_self" noStyles>
          <Flex className="items-center text-gray-600 hover:text-gray-700 text-bodySm">
            Back to top <Icon className="ml-1" icon={ICON_TYPE.CHEVRON_UP} />
          </Flex>
        </Link>
      </Flex>
      <Flex className="items-center mb-4">
        Variants:
        <Reference.Group className="ml-2">
          {variants.map((v) => {
            let href = "";
            switch (v) {
              case "responsive":
                href = "https://tailwindcss.com/docs/responsive-design";
                break;
              case "first":
                href =
                  "https://tailwindcss.com/docs/hover-focus-and-other-states#first-child";
                break;
              case "last":
                href =
                  "https://tailwindcss.com/docs/hover-focus-and-other-states#last-child";
                break;
              case "odd":
                href =
                  "https://tailwindcss.com/docs/hover-focus-and-other-states#odd-child";
                break;
              case "even":
                href =
                  "https://tailwindcss.com/docs/hover-focus-and-other-states#even-child";
                break;
              case "visited":
              case "checked":
              case "group-hover":
              case "group-focus":
              case "focus-within":
              case "hover":
              case "focus":
              case "focus-visible":
              case "active":
              case "disabled":
                href = `https://tailwindcss.com/docs/hover-focus-and-other-states#${v}`;
                break;
              case "dark":
                href = "https://tailwindcss.com/docs/dark-mode";
                break;
              default:
                break;
            }
            return (
              <Reference href={href} target="_blank" key={v}>
                {v}
              </Reference>
            );
          })}
        </Reference.Group>
      </Flex>
      <SimpleTable className="w-full">
        <SimpleTable.Head>
          <SimpleTable.Row>
            <SimpleTable.HeaderCell className="w-64">
              Classname
            </SimpleTable.HeaderCell>
            <SimpleTable.HeaderCell>Value</SimpleTable.HeaderCell>
          </SimpleTable.Row>
        </SimpleTable.Head>
        <SimpleTable.Body>
          {propertiesState
            ? propertiesStateArray.map((propState) => {
                return Object.keys(propState).map((key) => {
                  const value = propState[key];
                  return (
                    <PropertyRow key={key} propertyKey={key} value={value} />
                  );
                });
              })
            : null}
        </SimpleTable.Body>
      </SimpleTable>
    </Box>
  );
};

export const TailwindPluginList = ({
  columnCount,
}: {
  columnCount: number;
}) => {
  const tailwindPluginList = useTailwindPluginList();
  const itemsPerColumn = useMemo(
    () => Math.ceil(tailwindPluginList.length / columnCount),
    [tailwindPluginList.length, columnCount],
  );

  const dividedPluginList = useMemo(
    () =>
      tailwindPluginList.reduce<Array<string[]>>((acc, cur, i) => {
        const addToColumn = Math.floor(i / itemsPerColumn);
        let newArray = [...acc];
        const colExists = !!newArray[addToColumn];
        if (colExists) {
          newArray[addToColumn].push(cur);
        } else {
          newArray = [...newArray, [cur]];
        }
        return newArray;
      }, []),
    [itemsPerColumn, tailwindPluginList],
  );

  return (
    <Flex className="mb-10">
      {dividedPluginList.map((pl, i) => {
        return (
          <Flex className="flex-col items-start flex-grow" key={pl[i]}>
            {pl.map((p) => {
              const formattedName = camelToTitleCase(p);
              return (
                <Link
                  key={p}
                  href={`#${formattedName}`}
                  className="block mb-2"
                  target="_self"
                >
                  {formattedName}
                </Link>
              );
            })}
          </Flex>
        );
      })}
    </Flex>
  );
};

export const TailwindPropertiesFullList = () => {
  const tailwindPluginList = useTailwindPluginList();
  return (
    <Box>
      {tailwindPluginList.map((p) => {
        return <TailwindPropertyClasses key={p} pluginName={p} />;
      })}
    </Box>
  );
};
