import {
  createContext,
  useState,
  useContext,
  useMemo,
  useEffect,
  RefObject,
} from "react";



import contrast from "get-contrast";
import {
  blendWithBlack,
  blendWithWhite,
  getColorPaletteByVariation,
  getHighContrastColor,
} from "../util/widgetUtil";

/**
 * the provider type, typically with a children prop only
 */

export type BorderRadiusType = "Square" | "Rounded" | "Round";
export type FontType =
  | "Overpass"
  | "TimesNewRoman"
  | "Arial"
  | "CourierNew"
  | "TrebuchetMS"
  | "Verdana";

type ThemeProviderProps = {
  children: React.ReactNode;
  primaryColor?: string;
  secondaryColor?: string;
  borderRadius?: BorderRadiusType;
  fontType?: FontType;
  alternativeRoot?: RefObject<HTMLDivElement>;
};

/**
 * the context type, which provides the props and methods we can use in the components
 */
type ThemeProviderContextType = {
  currentPrimaryColor?: string;
  currentSecondaryColor?: string;
};
/**
 * the context type defaults
 */
const defaultAccordionContextType: ThemeProviderContextType = {
  currentPrimaryColor: undefined,
  currentSecondaryColor: undefined,
};

/**
 * create a new context of type {@link ThemeProviderContextType} using reacts {@link createContext}
 */
const ThemeContext = createContext<ThemeProviderContextType>(
  defaultAccordionContextType
);

/**
 * the provider which defines all the methods and properties, which are declared in {@link ThemeProviderContextType}
 * @param children the inner component, which can use the methods and properties
 * @returns an instance of {@link ThemeContext.Provider}
 */
export default function ThemeProvider({
  children,
  primaryColor,
  borderRadius,
  fontType,
  secondaryColor,
  alternativeRoot,
}: ThemeProviderProps) {
  const [currentPrimaryColor, setCurrentPrimaryColor] = useState(primaryColor);
  const [currentSecondaryColor, setCurrentSecondaryColor] =
    useState(secondaryColor);
  const [documentToManipulate, setDocumentToManipulate] =
    useState<HTMLElement>();

  useEffect(() => {
    setDocumentToManipulate(
      alternativeRoot?.current
        ? alternativeRoot.current
        : document.documentElement
    );
  }, [alternativeRoot?.current]);

  useEffect(() => {
    switch (fontType) {
      case "Arial":
        documentToManipulate?.style.setProperty(`--font`, "Arial");
        return;
      case "CourierNew":
        documentToManipulate?.style.setProperty(`--font`, "Courier New");
        return;
      case "TimesNewRoman":
        documentToManipulate?.style.setProperty(`--font`, "Times New Roman");
        return;
      case "TrebuchetMS":
        documentToManipulate?.style.setProperty(`--font`, "Trebuchet MS");
        return;
      case "Verdana":
        documentToManipulate?.style.setProperty(`--font`, "Verdana");
        return;
      case "Overpass":
      default:
        documentToManipulate?.style.setProperty(`--font`, "Overpass");
        return;
    }
  }, [fontType, documentToManipulate]);

  useEffect(() => {
    switch (borderRadius) {
      case "Square":
        documentToManipulate?.style.setProperty(`--border-radius`, "0px");
        return;
      case "Round":
        documentToManipulate?.style.setProperty(`--border-radius`, "1.5rem");
        return;
      case "Rounded":
      default:
        documentToManipulate?.style.setProperty(`--border-radius`, "0.5rem");
        return;
    }
  }, [borderRadius, documentToManipulate]);

  useEffect(() => {
    if (!primaryColor) return;

    const contrastToWhite = contrast.ratio("#fff", primaryColor);

    let primaryColorToUse = primaryColor;
    let secondaryColorToUse = secondaryColor;
    if (contrastToWhite < 3 && !secondaryColor) {
      primaryColorToUse = getHighContrastColor(primaryColor, 3.7);
      secondaryColorToUse = primaryColor;
    }

    if (!secondaryColorToUse) {
      const standardSecondaryColor = `#${
        getColorPaletteByVariation(primaryColor ?? "#ffffff")[2]
      }`;
      secondaryColorToUse = standardSecondaryColor;
      const contrastRatioPrimarySecondary = contrast.ratio(
        standardSecondaryColor,
        primaryColorToUse
      );
      if (contrastRatioPrimarySecondary < 2) {
        secondaryColorToUse = getHighContrastColor(primaryColorToUse, 3.7);
      }
    }

    setCurrentPrimaryColor(primaryColorToUse);
    setCurrentSecondaryColor(secondaryColorToUse);
  }, [primaryColor, secondaryColor]);

  const primaryColorPalette = useMemo(() => {
    if (!currentPrimaryColor) return;

    return {
      primary: currentPrimaryColor,
      hover: blendWithBlack(currentPrimaryColor, 48),
      active: blendWithBlack(currentPrimaryColor, 64),
    };
  }, [currentPrimaryColor]);

  const secondaryColorPalette = useMemo(() => {
    if (!currentSecondaryColor) return;

    return {
      primary: currentSecondaryColor,
      hover: blendWithWhite(currentSecondaryColor, 40),
      active: blendWithWhite(currentSecondaryColor, 50),
    };
  }, [currentSecondaryColor]);

  const hex60 = useMemo(() => {
    if (!currentPrimaryColor) return;
    return blendWithWhite(currentPrimaryColor, 42);
  }, [currentPrimaryColor]);

  const hex40 = useMemo(() => {
    if (!currentPrimaryColor) return;
    return blendWithWhite(currentPrimaryColor, 62);
  }, [currentPrimaryColor]);

  const hex20 = useMemo(() => {
    if (!currentPrimaryColor) return;
    return blendWithWhite(currentPrimaryColor, 82);
  }, [currentPrimaryColor]);

  const hex10 = useMemo(() => {
    if (!currentPrimaryColor) return;
    return blendWithWhite(currentPrimaryColor, 92);
  }, [currentPrimaryColor]);

  const akzentZwei = useMemo(() => {
    if (!currentPrimaryColor) return;
    return blendWithBlack(currentPrimaryColor, 72);
  }, [currentPrimaryColor]);

  const akzentEins = useMemo(() => {
    if (!currentPrimaryColor) return;
    return blendWithWhite(currentPrimaryColor, 85);
  }, [currentPrimaryColor]);

  useEffect(() => {
    if (
      !currentPrimaryColor ||
      !primaryColor ||
      !primaryColorPalette ||
      !secondaryColorPalette ||
      !hex10 ||
      !hex20 ||
      !hex40 ||
      !hex60 ||
      !akzentZwei ||
      !akzentEins
    ) {
      documentToManipulate?.style.setProperty(`--color-primary`, "#0066ff");
      documentToManipulate?.style.setProperty(
        `--color-primary-hover`,
        "#0005a3"
      );
      documentToManipulate?.style.setProperty(
        `--color-primary-active`,
        "#000068"
      );
      documentToManipulate?.style.setProperty(`--color-primary-60`, "#66a3ff");
      documentToManipulate?.style.setProperty(`--color-primary-40`, "#99c2ff");
      documentToManipulate?.style.setProperty(`--color-primary-20`, "#cce0ff");
      documentToManipulate?.style.setProperty(`--color-primary-10`, "#e6f0ff");
      documentToManipulate?.style.setProperty(`--color-akzent-zwei`, "#0036a3");
      documentToManipulate?.style.setProperty(`--color-akzent-eins`, "#3abedb");

      documentToManipulate?.style.setProperty(`--color-secondary`, "#d7dfff");
      documentToManipulate?.style.setProperty(
        `--color-secondary-hover`,
        "#b4c4fd"
      );
      documentToManipulate?.style.setProperty(
        `--color-secondary-active`,
        "#99abf8"
      );
      return;
    }

    documentToManipulate?.style.setProperty(
      `--color-primary`,
      primaryColorPalette.primary
    );
    documentToManipulate?.style.setProperty(
      `--color-primary-hover`,
      primaryColorPalette.hover
    );
    documentToManipulate?.style.setProperty(
      `--color-primary-active`,
      primaryColorPalette.active
    );
    documentToManipulate?.style.setProperty(`--color-primary-60`, hex60);
    documentToManipulate?.style.setProperty(`--color-primary-40`, hex40);
    documentToManipulate?.style.setProperty(`--color-primary-20`, hex20);
    documentToManipulate?.style.setProperty(`--color-primary-10`, hex10);
    documentToManipulate?.style.setProperty(`--color-akzent-zwei`, akzentZwei);
    documentToManipulate?.style.setProperty(`--color-akzent-eins`, akzentEins);

    documentToManipulate?.style.setProperty(
      `--color-secondary`,
      secondaryColorPalette.primary
    );
    documentToManipulate?.style.setProperty(
      `--color-secondary-hover`,
      secondaryColorPalette.hover
    );
    documentToManipulate?.style.setProperty(
      `--color-secondary-active`,
      secondaryColorPalette.active
    );
  }, [
    primaryColorPalette,
    secondaryColorPalette,
    hex60,
    hex40,
    hex20,
    hex10,
    documentToManipulate,
    primaryColor,
  ]);

  const value = useMemo(() => {
    return {
      currentPrimaryColor: currentPrimaryColor,
      currentSecondaryColor: currentSecondaryColor,
    };
  }, [currentSecondaryColor, currentPrimaryColor]);

  return (
    <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
  );
}

/**
 * the custom context hook, which is used to access the methods and props defined in the provider
 * within the children components
 * @returns a context hook
 */
export function useThemeContext() {
  return useContext(ThemeContext);
}
