import React, { useEffect, useMemo, useState } from "react";
import MUISlider from "@mui/material/Slider";
import FormControl, { useFormControl } from "@mui/material/FormControl";
import OutlinedInput from "@mui/material/OutlinedInput";
import InfoIcon from "@mui/icons-material/InfoOutlined";
import "./Sliders.styles.scss";

const DEFAULT_BELOW_THRESHOLD_COLOR = "#4259ee";
const DEFAULT_ABOVE_THRESHOLD_COLOR = "#ED9998";

export function Slider({
  headerOrientation = "top",
  header,
  label,
  inputPlaceholder,
  value = 0,
  onChange,
  required,
  disabled,
  min = 0,
  max = 100,
  variant = "default",
  inputPosition = "inline",
  colorChangeThreshold,
  valueFormat,
  messageTimeout = 5000,
  ...args
}) {
  const [infoMessage, setInfoMessage] = useState("");
  const [showInfo, setShowInfo] = useState(false);

  const isRangedSlider =
    Array.isArray(value) && variant == "ranged" && value.length == 2;
  let primaryValue;
  let secondaryValue;

  const [focusedInput, setFocusedInput] = useState(null);
  const [rightInputFocused, setRightInputFocused] = useState(false);
  const [formattedValue, setFormattedValue] = useState(value);

  if (Array.isArray(value) && value.length == 2) {
    primaryValue = value[0];
    secondaryValue = value[1];
  }

  const {
    valueForThreshold,
    isAboveThreshold,
    trackSplitPercent,
    thresholdMarkerLeft,
  } = useMemo(() => {
    const valueForThresh = Array.isArray(value) ? Math.max(...value) : value;
    if (colorChangeThreshold == null) {
      return {
        valueForThreshold: valueForThresh,
        isAboveThreshold: false,
        trackSplitPercent: 0,
        thresholdMarkerLeft: null,
      };
    }
    const isRanged = Array.isArray(value) && value.length === 2;
    const primary = isRanged ? value[0] : undefined;
    const secondary = isRanged ? value[1] : undefined;
    const aboveThresh = valueForThresh > colorChangeThreshold;

    let split;
    if (isRanged) {
      if (colorChangeThreshold <= primary) split = 0;
      else if (colorChangeThreshold >= secondary) split = 100;
      else
        split =
          ((colorChangeThreshold - primary) / (secondary - primary)) * 100;
    } else {
      split =
        aboveThresh && valueForThresh > min
          ? ((colorChangeThreshold - min) / (valueForThresh - min)) * 100
          : 0;
    }

    const markerLeft =
      colorChangeThreshold > min && colorChangeThreshold < max
        ? ((colorChangeThreshold - min) / (max - min)) * 100
        : null;
    return {
      valueForThreshold: valueForThresh,
      isAboveThreshold: aboveThresh,
      trackSplitPercent: split,
      thresholdMarkerLeft: markerLeft,
    };
  }, [value, min, max, colorChangeThreshold]);

  const handleInputChange = (e, key) => {
    const value = e.target.value;

    // Clear info message when user starts typing
    if (showInfo) {
      setShowInfo(false);
      setInfoMessage("");
    }

    // Ignore if the value is not a number
    if (isNaN(value)) {
      setInfoMessage("Please enter a valid number");
      setShowInfo(true);
      return;
    }
    e.persist(); // unpool the event

    // passing fakeEvent to maintain stability of values received from slider and input field
    const fakeEvent = {
      ...e,
      target: {
        ...e.target,
        value: [],
      },
    };
    if (key == "min") {
      if (Number(value) < min || Number(value) > secondaryValue) {
        const message =
          Number(value) < min
            ? `Value must be at least ${min}`
            : `Initial value must be less than the maximum value`;
        setInfoMessage(message);
        setShowInfo(true);
        return;
      }
      fakeEvent.target.value = [Number(value), secondaryValue];
      onChange(fakeEvent, key);
    } else if (key == "max") {
      if (Number(value) > max || Number(value) < primaryValue) {
        const message =
          Number(value) > max
            ? `Value must not exceed ${max}`
            : `Maximum value must be greater than the initial value`;
        setInfoMessage(message);
        setShowInfo(true);
        return;
      }
      fakeEvent.target.value = [primaryValue, Number(value)];
      onChange(fakeEvent, key);
    } else {
      onChange(e, key);
    }
  };

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

    const [pos, symbol] = valueFormat;

    const format = (v) => {
      const formatValue = (val) =>
        pos === "left" ? `${symbol}${val}` : `${val}${symbol}`;
      return Array.isArray(v) ? v.map(formatValue) : formatValue(v);
    };

    setFormattedValue(format(value));
  }, [valueFormat, value]);

  const handleFocusChange = (e) => {
    setFocusedInput(true);
  };

  const handleBlurChange = (e) => {
    setFocusedInput(null);
  };

  useEffect(() => {
    if (showInfo) {
      setTimeout(() => {
        setShowInfo(false);
      }, messageTimeout);
    }
  }, [showInfo]);

  return (
    <div className="slider-container">
      <div
        className={`impact_slider_layout slider-container${
          headerOrientation === "top" ? "header-top" : "header-left"
        } input-position-${inputPosition}`}
      >
        {isRangedSlider && inputPosition == "inline" && (
          <div className="input_layout_style ranged ranged-start">
            <form noValidate autoComplete="off">
              <FormControl>
                <OutlinedInput
                  value={
                    !focusedInput && valueFormat
                      ? isRangedSlider
                        ? formattedValue[0]
                        : formattedValue
                      : isRangedSlider
                      ? primaryValue
                      : value
                  }
                  onChange={(e) => {
                    handleInputChange(e, isRangedSlider ? "min" : "");
                  }}
                  disabled={disabled}
                  onFocus={handleFocusChange}
                  onBlur={handleBlurChange}
                />
              </FormControl>
            </form>
          </div>
        )}
        <div className="impact_slider_container">
          {header && (
            <div className="impact_slider_layout_heading">{header}</div>
          )}
          {label && (
            <label className="impact_slider_labelname">
              {label} {required && <span style={{ color: "red" }}>*</span>}
            </label>
          )}
          <div
            className={`impact_slider_main_container ${
              disabled && "impact_slider_disabled"
            }`}
          >
            {colorChangeThreshold && (
              <span
                className="impact_slider_threshold_marker"
                style={{
                  left: `${thresholdMarkerLeft}%`,
                }}
                aria-hidden
              />
            )}
            <MUISlider
              {...args}
              defaultValue={value}
              value={value}
              onChange={onChange}
              disabled={disabled}
              max={max}
              min={min}
              aria-label="Default"
              valueLabelDisplay="auto"
              sx={
                isAboveThreshold
                  ? {
                      color: DEFAULT_BELOW_THRESHOLD_COLOR,
                      "& .MuiSlider-track": {
                        background: `linear-gradient(to right, ${DEFAULT_BELOW_THRESHOLD_COLOR} 0%, ${DEFAULT_BELOW_THRESHOLD_COLOR} ${trackSplitPercent}%, ${DEFAULT_ABOVE_THRESHOLD_COLOR} ${trackSplitPercent}%, ${DEFAULT_ABOVE_THRESHOLD_COLOR} 100%)`,
                        border: "none",
                        transition: "none",
                      },
                      "& .MuiSlider-thumb": {
                        backgroundColor: DEFAULT_BELOW_THRESHOLD_COLOR,
                        zIndex: 2,
                      },
                    }
                  : undefined
              }
            />
          </div>
        </div>

        <div
          className={`input_layout_style ${
            isRangedSlider ? "ranged" : "single"
          }`}
        >
          <form noValidate autoComplete="off">
            <FormControl>
              {(!isRangedSlider || inputPosition == "bottom") && (
                <OutlinedInput
                  value={
                    !focusedInput && valueFormat
                      ? formattedValue
                      : isRangedSlider
                      ? primaryValue
                      : value
                  }
                  onChange={(e) => {
                    handleInputChange(e, isRangedSlider ? "min" : "");
                  }}
                  disabled={disabled}
                  onFocus={handleFocusChange}
                  onBlur={handleBlurChange}
                />
              )}
              {isRangedSlider && (
                <OutlinedInput
                  value={
                    !rightInputFocused && valueFormat
                      ? formattedValue[1]
                      : secondaryValue
                  }
                  onChange={(e) => {
                    handleInputChange(e, "max");
                  }}
                  disabled={disabled}
                  onFocus={() => setRightInputFocused(true)}
                  onBlur={() => setRightInputFocused(false)}
                />
              )}
            </FormControl>
          </form>
        </div>
      </div>
      <div className={`slider-info-message ${!showInfo ? "hidden" : ""}`}>
        {showInfo && (
          <>
            <InfoIcon className="slider-info-icon" />
            <span className="slider-info-text">{infoMessage}</span>
          </>
        )}
      </div>
    </div>
  );
}
