import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classNames from 'clsx';
import type { RgbaColor } from 'colord';
import { colord } from 'colord';
import { useTheme } from '@mui/material/styles';
import { SquareRounded, UnfoldMoreRounded } from '@mui/icons-material';

import { SHAPE_ROUNDED } from '@/mui/constants';

import { debounce } from '@/modules/@common/helpers/events';
import type { HsvaColor } from '@/mui/helpers/color';
import {
  alphaToPercentages,
  equalHsva,
  hsvaToRgba,
  normalizeHsva,
  rgbaToHsva,
  rgbaToString,
  toHex,
} from '@/mui/helpers/color';

import Button from '../../Button/Button';
import IconButton from '../../IconButton/IconButton';
import Paper from '../../Paper/Paper';
import Stack from '../../Stack/Stack';
import TextField from '../../TextField/TextField';
import Typography from '../../Typography/Typography';
import AlphaControl from '@/mui/components/@extendedComponents/ColorPicker/components/AlphaControl/AlphaControl';
import HueControl from '@/mui/components/@extendedComponents/ColorPicker/components/HueControl/HueControl';
import SaturationControl from '@/mui/components/@extendedComponents/ColorPicker/components/SaturationControl/SaturationControl';

import styles from './ColorPicker.module.scss';

type StaticColorPickerProps = {
  color?: string;
  disableAlpha?: boolean;
  disabled?: boolean;
  elevation?: number;
  presetColors?: string[];
  onChange: (color: { hex: string; rgb: RgbaColor }) => void;
};

const StaticColorPicker = forwardRef<HTMLDivElement, StaticColorPickerProps>((props, ref) => {
  const theme = useTheme();
  const color = props.color && colord(props.color).isValid() ? props.color : '#fff';
  const initialHsva = useMemo(
    () => normalizeHsva(rgbaToHsva(colord(color).toRgb()), props.disableAlpha),
    [color, props.disableAlpha],
  );
  const [hsva, setHsva] = useState<HsvaColor>(initialHsva);
  const [hexMode, setHexMode] = useState(true);

  const [hexValue, setHexValue] = useState(() => colord(hsvaToRgba(initialHsva)).toHex());
  const [alphaValue, setAlphaValue] = useState(alphaToPercentages(initialHsva.a));

  const [rValue, setRValue] = useState('0');
  const [gValue, setGValue] = useState('0');
  const [bValue, setBValue] = useState('0');
  const [aValue, setAValue] = useState('1');

  const lastEmittedHex = useRef<string>(toHex(hsvaToRgba(initialHsva), props.disableAlpha));

  const updateHsva = useCallback(
    (partial: Partial<HsvaColor>) => {
      setHsva((prev) => {
        const next = normalizeHsva({ ...prev, ...partial }, props.disableAlpha);
        return equalHsva(prev, next) ? prev : next;
      });
    },
    [props.disableAlpha],
  );

  const applyColorString = useCallback(
    (value: string) => {
      const parsed = colord(value);
      if (!parsed.isValid()) return;
      const rgb = parsed.toRgb();
      updateHsva(
        rgbaToHsva({
          ...rgb,
          a: props.disableAlpha ? 1 : rgb.a,
        }),
      );
    },
    [props.disableAlpha, updateHsva],
  );

  const onChangeComplete = useMemo(
    () =>
      debounce((next: HsvaColor) => {
        const nextHex = toHex(hsvaToRgba(next), props.disableAlpha);
        if (nextHex === lastEmittedHex.current) return;
        lastEmittedHex.current = nextHex;
        props.onChange({
          hex: nextHex,
          rgb: hsvaToRgba(next),
        });
      }, 200),
    [props.disableAlpha, props.onChange],
  );

  useEffect(() => {
    const nextHsva = normalizeHsva(rgbaToHsva(colord(color).toRgb()), props.disableAlpha);
    if (!equalHsva(nextHsva, hsva)) {
      setHsva(nextHsva);
    }
    lastEmittedHex.current = toHex(hsvaToRgba(nextHsva), props.disableAlpha);
  }, [color, props.disableAlpha]);

  useEffect(() => {
    onChangeComplete(hsva);
    return () => {
      onChangeComplete.clear();
    };
  }, [hsva, onChangeComplete]);

  const rgba = useMemo(() => hsvaToRgba(hsva), [hsva]);
  const rgbaString = useMemo(() => rgbaToString(rgba), [rgba]);

  useEffect(() => {
    const rgbaCurrent = hsvaToRgba(hsva);
    setHexValue(colord(rgbaCurrent).toHex());
    setAlphaValue(alphaToPercentages(rgbaCurrent.a));
    setRValue(`${rgbaCurrent.r}`);
    setGValue(`${rgbaCurrent.g}`);
    setBValue(`${rgbaCurrent.b}`);
    setAValue(`${rgbaCurrent.a}`);
  }, [hsva]);

  return (
    <Paper
      component={Stack}
      spacing={3}
      className={classNames(styles.ColorPicker, {
        [styles.ColorPicker___disabled]: props.disabled,
        [styles.ColorPicker___disableAlpha]: props.disableAlpha,
      })}
      elevation={props.elevation ?? 1}
      ref={ref}
    >
      <Stack spacing={2} direction="column" fullWidth>
        <SaturationControl hsva={hsva} onChange={updateHsva} />
        <HueControl hsva={hsva} onChange={updateHsva} />
        {!props.disableAlpha && <AlphaControl hsva={hsva} onChange={updateHsva} />}
      </Stack>
      <Stack spacing={2} direction="row" alignItems="flex-start" justifyContent="space-between">
        <IconButton
          size="small"
          readOnly
          tabIndex={-1}
          style={
            {
              '--color-preview-bg': rgbaString,
              borderRadius: theme.shape.borderRadius,
            } as React.CSSProperties
          }
          className={styles.PreviewButton}
        />
        {hexMode ? (
          <Stack spacing={1} flex={1}>
            <Stack spacing={2} direction="row" alignItems="center" flex={1}>
              <Stack spacing={1} flex={1}>
                <TextField
                  value={hexValue}
                  size="small"
                  margin="none"
                  textAlign="center"
                  shape={SHAPE_ROUNDED}
                  onBlur={(event) => {
                    const { value } = event.target;
                    const parsed = colord(value);
                    if (!parsed.isValid()) {
                      setHexValue(colord(rgba).toHex());
                      return;
                    }
                    const rgb = parsed.toRgb();
                    updateHsva(
                      rgbaToHsva({
                        ...rgb,
                        a: props.disableAlpha ? 1 : alphaValue / 100,
                      }),
                    );
                  }}
                  onChange={(event) => {
                    setHexValue(event.target.value);
                  }}
                  onKeyUp={(event) => {
                    if (event.key === 'Enter') {
                      const parsed = colord(hexValue);
                      if (parsed.isValid()) {
                        const rgb = parsed.toRgb();
                        updateHsva(
                          rgbaToHsva({
                            ...rgb,
                            a: props.disableAlpha ? 1 : alphaValue / 100,
                          }),
                        );
                      }
                    }
                  }}
                />
                <Typography align="center" color="text.primary" variant="caption">
                  HEX
                </Typography>
              </Stack>
              {!props.disableAlpha && (
                <Stack spacing={1} flex={0.3}>
                  <TextField
                    type="number"
                    size="small"
                    shape={SHAPE_ROUNDED}
                    margin="none"
                    textAlign="center"
                    inputProps={{
                      min: 0,
                      step: 1,
                      max: 100,
                    }}
                    value={alphaValue}
                    onChange={(event) => {
                      let parsedValue = +event.target.value;
                      if (Number.isNaN(parsedValue) || parsedValue < 0) {
                        parsedValue = 0;
                      } else if (parsedValue > 100) {
                        parsedValue = 100;
                      }
                      setAlphaValue(parsedValue);
                      updateHsva({ a: parsedValue / 100 });
                    }}
                  />
                  <Typography align="center" color="text.primary" variant="caption">
                    %
                  </Typography>
                </Stack>
              )}
            </Stack>
          </Stack>
        ) : (
          <Stack spacing={1} direction="row" alignItems="flex-start" flex={1}>
            {(
              [
                { key: 'r', value: rValue, setValue: setRValue, min: 0, max: 255, step: 1 },
                { key: 'g', value: gValue, setValue: setGValue, min: 0, max: 255, step: 1 },
                { key: 'b', value: bValue, setValue: setBValue, min: 0, max: 255, step: 1 },
                ...(props.disableAlpha
                  ? []
                  : [{ key: 'a', value: aValue, setValue: setAValue, min: 0, max: 1, step: 0.01 }]),
              ] as Array<{
                key: 'r' | 'g' | 'b' | 'a';
                value: string;
                setValue: (value: string) => void;
                min: number;
                max: number;
                step: number;
              }>
            ).map((input) => (
              <Stack key={input.key} spacing={1} justifyContent="center" flex={1}>
                <TextField
                  margin="none"
                  textAlign="center"
                  type="number"
                  size="small"
                  shape={SHAPE_ROUNDED}
                  inputProps={{
                    min: input.min,
                    step: input.step,
                    max: input.max,
                  }}
                  value={input.value}
                  onChange={(event) => {
                    let resValue = +event.target.value;
                    if (Number.isNaN(resValue) || resValue < input.min) {
                      resValue = input.min;
                    } else if (resValue > input.max) {
                      resValue = input.max;
                    }
                    input.setValue(`${resValue}`);
                    updateHsva(
                      rgbaToHsva({
                        ...rgba,
                        [input.key]: resValue,
                      }),
                    );
                  }}
                />
                <Typography align="center" color="text.primary" variant="caption">
                  {input.key.toUpperCase()}
                </Typography>
              </Stack>
            ))}
          </Stack>
        )}
        <Button
          size="small"
          square
          variant="text"
          shape={SHAPE_ROUNDED}
          color="neutral"
          startIcon={<UnfoldMoreRounded />}
          onClick={() => setHexMode((prev) => !prev)}
        />
      </Stack>
      {!!props.presetColors?.length && (
        <Stack spacing={theme.spacing(1, 2)} justifyContent="flex-start" direction="row" flexWrap="wrap">
          {props.presetColors.map((presetColor$) => {
            const proceedColor = colord(presetColor$).toRgb();
            const previewColor = toHex(proceedColor, props.disableAlpha);

            return (
              <Button
                key={presetColor$}
                size="xSmall"
                square
                variant="text"
                color="neutral"
                onClick={() => applyColorString(previewColor)}
                startIcon={<SquareRounded className={styles.PresetButtonColor} htmlColor={previewColor} />}
              />
            );
          })}
        </Stack>
      )}
    </Paper>
  );
});

StaticColorPicker.displayName = 'StaticColorPicker';

export default StaticColorPicker;
