import Button from "@/components/Low/Button";
import Input from "@/components/Low/Input";
import Select from "@/components/Low/Select";
import Separator from "@/components/Low/Separator";
import { Grading, Step } from "@/interfaces";
import { Entity } from "@/interfaces/entity";
import { User } from "@/interfaces/user";
import {
  CEFR_STEPS,
  GENERAL_STEPS,
  IELTS_STEPS,
  TOFEL_STEPS,
} from "@/resources/grading";
import { checkAccess } from "@/utils/permissions";
import axios from "axios";
import clsx from "clsx";
import {
  Dispatch,
  memo,
  SetStateAction,
  useCallback,
  useEffect,
  useState,
} from "react";
import { BsPlusCircle, BsTrash } from "react-icons/bs";
import { toast } from "react-toastify";

const areStepsOverlapped = (steps: Step[]) => {
  for (let i = 0; i < steps.length; i++) {
    if (i === 0) continue;

    const step = steps[i];
    const previous = steps[i - 1];

    if (previous.max >= step.min) return true;
  }

  return false;
};
interface RowProps {
  min: number;
  max: number;
  index: number;
  label: string;
  isLast: boolean;
  isLoading: boolean;
  setSteps: Dispatch<SetStateAction<Step[]>>;
  addRow: (index: number) => void;
}

function GradingRow({
  min,
  max,
  label,
  index,
  isLoading,
  isLast,
  setSteps,
  addRow,
}: RowProps) {
  const onChangeMin = useCallback(
    (e: string) => {
      setSteps((prev) =>
        prev.map((x, i) => (i === index ? { ...x, min: parseInt(e) } : x))
      );
    },
    [index, setSteps]
  );

  const onChangeMax = useCallback(
    (e: string) => {
      setSteps((prev) =>
        prev.map((x, i) => (i === index ? { ...x, max: parseInt(e) } : x))
      );
    },
    [index, setSteps]
  );

  const onChangeLabel = useCallback(
    (e: string) => {
      setSteps((prev) =>
        prev.map((x, i) => (i === index ? { ...x, label: e } : x))
      );
    },
    [index, setSteps]
  );

  const onAddRow = useCallback(() => addRow(index), [addRow, index]);

  const removeRow = useCallback(
    () => setSteps((prev) => prev.filter((_, i) => i !== index)),
    [index, setSteps]
  );
  return (
    <>
      <div className="flex items-center gap-4">
        <div className="grid grid-cols-3 gap-4 w-full">
          <Input
            label="Min. Percentage"
            value={min}
            type="number"
            disabled={index === 0 || isLoading}
            onChange={onChangeMin}
            name="min"
          />
          <Input
            label="Grade"
            value={label}
            type="text"
            disabled={isLoading}
            onChange={onChangeLabel}
            name="min"
          />
          <Input
            label="Max. Percentage"
            value={max}
            type="number"
            disabled={isLast || isLoading}
            onChange={onChangeMax}
            name="max"
          />
        </div>
        {index !== 0 && !isLast && (
          <button
            disabled={isLoading}
            className="pt-9 text-xl group"
            onClick={removeRow}
          >
            <div className="w-full h-full flex items-center justify-center group-hover:bg-neutral-200 rounded-full p-3 transition ease-in-out duration-300">
              <BsTrash />
            </div>
          </button>
        )}
      </div>

      {!isLast && (
        <Button
          className="w-full flex items-center justify-center"
          disabled={isLoading}
          onClick={onAddRow}
        >
          <BsPlusCircle />
        </Button>
      )}
    </>
  );
}
const GradingRowMemo = memo(GradingRow);
interface Props {
  user: User;
  entitiesGrading: Grading[];
  entities: Entity[];
  mutate: () => void;
}

export default function CorporateGradingSystem({
  user,
  entitiesGrading = [],
  entities = [],
  mutate,
}: Props) {
  const [entity, setEntity] = useState(entitiesGrading[0]?.entity || undefined);
  const [isLoading, setIsLoading] = useState(false);
  const [steps, setSteps] = useState<Step[]>([]);
  const [otherEntities, setOtherEntities] = useState<string[]>([]);

  useEffect(() => {
    if (entity) {
      const entitySteps = entitiesGrading.find(
        (e) => e.entity === entity
      )!.steps;
      setSteps(entitySteps || []);
    }
  }, [entitiesGrading, entity]);

  const saveGradingSystem = () => {
    if (!steps.every((x) => x.min < x.max))
      return toast.error(
        "One of your steps has a minimum threshold inferior to its superior threshold."
      );
    if (areStepsOverlapped(steps))
      return toast.error("There seems to be an overlap in one of your steps.");
    if (
      steps.reduce((acc, curr) => {
        return acc - (curr.max - curr.min + 1);
      }, 100) > 0
    )
      return toast.error("There seems to be an open interval in your steps.");

    setIsLoading(true);
    axios
      .post("/api/grading", { user: user.id, entity, steps })
      .then(() => toast.success("Your grading system has been saved!"))
      .then(mutate)
      .catch(() => toast.error("Something went wrong, please try again later"))
      .finally(() => setIsLoading(false));
  };

  const applyToOtherEntities = () => {
    if (!steps.every((x) => x.min < x.max))
      return toast.error(
        "One of your steps has a minimum threshold inferior to its superior threshold."
      );
    if (areStepsOverlapped(steps))
      return toast.error("There seems to be an overlap in one of your steps.");
    if (
      steps.reduce((acc, curr) => {
        return acc - (curr.max - curr.min + 1);
      }, 100) > 0
    )
      return toast.error("There seems to be an open interval in your steps.");

    if (otherEntities.length === 0)
      return toast.error("Select at least one entity");

    setIsLoading(true);
    axios
      .post("/api/grading/multiple", {
        user: user.id,
        entities: otherEntities,
        steps,
      })
      .then(() => toast.success("Your grading system has been saved!"))
      .then(mutate)
      .catch(() => toast.error("Something went wrong, please try again later"))
      .finally(() => setIsLoading(false));
  };

  const addRow = useCallback((index: number) => {
    setSteps((prev) => {
      const item = {
        min: prev[index === 0 ? 0 : index - 1].max + 1,
        max: prev[index + 1].min - 1,
        label: "",
      };
      return [
        ...prev.slice(0, index + 1),
        item,
        ...prev.slice(index + 1, prev.length),
      ];
    });
  }, []);

  return (
    <div className="flex flex-col gap-4 border p-4 border-mti-gray-platinum rounded-xl">
      <label className="font-normal text-base text-mti-gray-dim">
        Grading System
      </label>
      <div className={clsx("flex flex-col gap-4")}>
        <label className="font-normal text-base text-mti-gray-dim">
          Entity
        </label>
        <Select
          defaultValue={{
            value: (entities || [])[0]?.id,
            label: (entities || [])[0]?.label,
          }}
          options={entities.map((e) => ({ value: e.id, label: e.label }))}
          onChange={(e) => setEntity(e?.value || undefined)}
          isClearable={checkAccess(user, ["admin", "developer"])}
        />
      </div>

      {entities.length > 1 && (
        <>
          <Separator />
          <label className="font-normal text-base text-mti-gray-dim">
            Apply this grading system to other entities
          </label>
          <Select
            options={entities.map((e) => ({ value: e.id, label: e.label }))}
            onChange={(e) =>
              !e
                ? setOtherEntities([])
                : setOtherEntities(e.map((o) => o.value!))
            }
            isMulti
          />
          <Button
            onClick={applyToOtherEntities}
            isLoading={isLoading}
            disabled={isLoading || otherEntities.length === 0}
            variant="outline"
          >
            Apply to {otherEntities.length} other entities
          </Button>
          <Separator />
        </>
      )}

      <label className="font-normal text-base text-mti-gray-dim">
        Preset Systems
      </label>
      <div className="grid grid-cols-4 gap-4">
        <Button variant="outline" onClick={() => setSteps(CEFR_STEPS)}>
          CEFR
        </Button>
        <Button variant="outline" onClick={() => setSteps(GENERAL_STEPS)}>
          General English
        </Button>
        <Button variant="outline" onClick={() => setSteps(IELTS_STEPS)}>
          IELTS
        </Button>
        <Button variant="outline" onClick={() => setSteps(TOFEL_STEPS)}>
          TOFEL iBT
        </Button>
      </div>

      {steps.map((step, index) => (
        <GradingRowMemo
          key={index}
          min={step.min}
          max={step.max}
          label={step.label}
          index={index}
          isLoading={isLoading}
          isLast={index === steps.length - 1}
          setSteps={setSteps}
          addRow={addRow}
        />
      ))}

      <Button
        onClick={saveGradingSystem}
        isLoading={isLoading}
        disabled={isLoading}
        className="mt-8"
      >
        Save Grading System
      </Button>
    </div>
  );
}
