import Joi from "joi";
import React, { useEffect } from "react";
import { Navigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import { useTranslation } from "react-i18next";
import { Namespaces } from "../../../i18n/i18n";
import { InputType } from "../../../components/common/Input";
import {
  renderInput,
  renderButton,
  renderError,
  renderSelect,
  renderInputNumber,
  renderInputTextarea,
  renderGlossaryPicker,
  renderSequencePicker,
} from "../../../components/common/formHelpers";
import { MakeGeneralIdRef } from "../../../utils/GeneralIdRef";
import customFieldsService, {
  numberParams,
  textParams,
} from "./services/customFieldsService";
import Option from "../../../components/common/option";
import { GeneralIdRef } from "./../../../utils/GeneralIdRef";
import {
  CustomFieldValue,
  SystemGlossaries,
} from "../glossary/services/glossaryService";
import Loading from "../../../components/common/Loading";
import { useFormWithGuard } from "../../../components/common/useFormRouter";

interface CustomFieldDetailsProps {
  editMode?: boolean;
}

const CustomFieldDetails: React.FC<CustomFieldDetailsProps> = ({
  editMode = false,
}) => {
  const { customFieldId } = useParams<{ customFieldId: string }>();
  const { t } = useTranslation(Namespaces.Common);

  const labelName = t("Name");
  const labelFieldType = t("FieldType");
  const labelMultiLine = t("MultiLine");
  const labelDefaultValue = t("DefaultValue");
  const labelMinValue = t("MinimumValue");
  const labelMaxValue = t("MaximumValue");
  const labelStep = t("Step");
  const labelRequired = t("Required");
  const labelMinEntries = t("MinEntries");
  const labelMaxEntries = t("MaxEntriesEmptyUnlimited");
  let labelRefElementId = t("SequenceFormGlossary");

  const labelApply = t("Save");
  const labelSave = t("SaveAndClose");

  const form = useFormWithGuard({
    loaded: false,
    data: {
      name: "",
      fieldType: "Text",
      defaultValue: "",
      multiLine: false,
      minEntries: 0,
      maxEntries: 1,
      refElementId: undefined,
      minValue: undefined,
      maxValue: undefined,
      step: undefined,
      required: false,
    },
    errors: {},
    redirect: "",
  });

  form.schema = {
    name: Joi.string().required().max(450).label(labelName),
    fieldType: Joi.string().required().label(labelFieldType),
    multiLine: Joi.boolean().label(labelMultiLine),
    minEntries: Joi.number().min(0).label(labelMinEntries),
    maxEntries: Joi.number().empty("").label(labelMaxEntries),
    refElementId: Joi.when("fieldType", {
      is: Joi.string().valid("Sequence"),
      then: Joi.object({
        id: Joi.optional(),
        guid: Joi.optional(),
      }).required(),
    }).when("fieldType", {
      is: Joi.string().valid("Glossary"),
      then: Joi.array()
        .min(1)
        .items(
          Joi.object({
            displayValue: Joi.string().optional(),
            value: Joi.object({
              id: Joi.optional(),
              guid: Joi.optional(),
            }).required(),
          }),
        )
        .required(),
    }),
    minValue: Joi.number().allow("").label(labelMinValue),
    maxValue: Joi.number().allow("").label(labelMaxValue),
    step: Joi.number().optional().allow("").min(0).label(labelStep),
    required: Joi.boolean().label(labelRequired),

    //defaultValue: Joi.string().allow("").label(labelDefaultValue)

    defaultValue: Joi.when("fieldType", {
      is: Joi.string().valid("Number"),
      then: Joi.when("minValue", {
        is: Joi.any().valid(null, ""),
        then: Joi.number(),
        otherwise: Joi.number()
          .min(Joi.ref("minValue"))
          .message(t("DefaultMustBeGreaterThanOrEqualToMinimumValue")),
      })
        .when("maxValue", {
          is: Joi.any().valid(null, ""),
          then: Joi.number(),
          otherwise: Joi.number()
            .max(Joi.ref("maxValue"))
            .message(t("DefaultMustBeLessThanOrEqualToMaximumValue")),
        })
        .allow(""),
      otherwise: Joi.string().allow(""),
    }).label(labelDefaultValue),
  };

  const isEditMode = () => {
    return editMode;
  };

  useEffect(() => {
    const loadData = async () => {
      if (customFieldId !== undefined) {
        try {
          const loadedData = await customFieldsService.getField(
            BigInt(customFieldId),
          );

          if (loadedData) {
            const newData = { ...form.state.data };
            newData.name = loadedData.name;
            newData.fieldType = loadedData.fieldType;
            newData.defaultValue = loadedData.defaultValue;
            newData.minEntries = loadedData.minEntries;
            newData.maxEntries = loadedData.maxEntries;
            switch (newData.fieldType) {
              case "Glossary":
                let convertedRefElementId: CustomFieldValue = {
                  value: loadedData.refElementId,
                };

                newData.refElementId = [convertedRefElementId];
                newData.required = loadedData.minEntries > 0;
                break;
              case "Sequence":
                newData.refElementId = loadedData.refElementId;
                break;
              case "Domain":
                newData.required = loadedData.minEntries > 0;
                break;
            }

            if (loadedData.parameters !== undefined) {
              switch (newData.fieldType) {
                case "Number":
                  newData.required = loadedData.minEntries > 0;
                  const parameters: numberParams = JSON.parse(
                    loadedData.parameters,
                  );
                  newData.minValue = parameters.minValue ?? undefined;
                  newData.maxValue = parameters.maxValue ?? undefined;
                  newData.step = parameters.step ?? undefined;
                  break;
                case "Text":
                  const textParameters: textParams = JSON.parse(
                    loadedData.parameters,
                  );
                  newData.multiLine = textParameters.multiLine ?? false;
                  break;
              }
            }

            form.setState({ loaded: true, data: newData });
          } else {
            form.setState({ loaded: false });
          }
        } catch (ex: any) {
          form.handleGeneralError(ex);
        }
      }

      if (!editMode) form.setState({ loaded: true });
    };

    loadData();
  }, [customFieldId, editMode]); // eslint-disable-line react-hooks/exhaustive-deps

  const doSubmit = async (buttonName: string) => {
    try {
      const { name, fieldType } = form.state.data;
      const nameStr = typeof name === "string" ? name : "";
      const fieldTypeStr = typeof fieldType === "string" ? fieldType : "";
      let { refElementId, defaultValue, minEntries, maxEntries, required } =
        form.state.data;
      let numberParams: numberParams | undefined = undefined;
      let textParams: textParams | undefined = undefined;
      let params;
      let refElementIdValue: GeneralIdRef | undefined;

      switch (fieldTypeStr) {
        case "Sequence":
          minEntries = 1;
          maxEntries = 1;
          defaultValue = "";
          refElementIdValue = refElementId as GeneralIdRef;
          break;
        case "FormTemplate":
          minEntries = 1;
          maxEntries = 1;
          defaultValue = "";
          break;
        case "Domain":
          minEntries = required ? 1 : 0;
          maxEntries = maxEntries === 0 ? undefined : maxEntries;
          defaultValue = "";
          break;
        case "Glossary":
          minEntries = required ? 1 : 0;
          maxEntries = maxEntries === 0 ? undefined : maxEntries;
          defaultValue = "";
          refElementIdValue = (refElementId as CustomFieldValue[])[0]
            .value as GeneralIdRef;
          break;
        case "Text":
          minEntries = 1;
          maxEntries = 1;
          let { multiLine } = form.state.data;
          textParams = { multiLine: Boolean(multiLine) };
          params = textParams;
          refElementIdValue = undefined;
          break;
        case "Number":
          refElementIdValue = undefined;
          let { minValue, maxValue, step } = form.state.data;
          const minValueNum =
            minValue === null || minValue === undefined || minValue === ""
              ? undefined
              : Number(minValue);
          const maxValueNum =
            maxValue === null || maxValue === undefined || maxValue === ""
              ? undefined
              : Number(maxValue);
          const stepNum =
            step === null || step === undefined || step === ""
              ? undefined
              : Number(step);
          numberParams = {
            minValue: minValueNum,
            maxValue: maxValueNum,
            step: stepNum,
          };
          params = numberParams;
          minEntries = required ? 1 : 0;
          maxEntries = 1;
          break;
        default:
          refElementIdValue = undefined;
      }

      const minEntriesValue =
        typeof minEntries === "number"
          ? minEntries
          : minEntries === undefined || minEntries === null || minEntries === ""
            ? 0
            : Number(minEntries);
      const cleanMaxEntries: Number | undefined =
        maxEntries === "" ? undefined : Number(maxEntries);

      const defaultValueStr =
        typeof defaultValue === "string"
          ? defaultValue
          : defaultValue === undefined || defaultValue === null
            ? ""
            : String(defaultValue);

      if (isEditMode()) {
        var generalIdRef = MakeGeneralIdRef(BigInt(customFieldId!));
        const response = await customFieldsService.putField(
          generalIdRef,
          nameStr,
          fieldTypeStr,
          defaultValueStr,
          minEntriesValue,
          cleanMaxEntries,
          refElementIdValue,
          params,
        );
        if (response) {
          toast.info(t("CustomFieldEdited"));
        }
      } else {
        const response = await customFieldsService.postField(
          nameStr,
          fieldTypeStr,
          defaultValueStr,
          minEntriesValue,
          cleanMaxEntries,
          refElementIdValue,
          params,
        );
        if (response) {
          toast.info(t("NewCustomFieldAdded"));
        }
      }

      if (buttonName === "save") form.setState({ redirect: "/customfields" });

      form.markAsSaved();
    } catch (ex: any) {
      form.handleGeneralError(ex);
    }
  };

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    form.handleSubmit(e, doSubmit);
  };

  if (form.state.redirect) return <Navigate to={form.state.redirect} />;

  const { fieldType } = form.state.data;
  const fieldTypeValue = typeof fieldType === "string" ? fieldType : "";

  let mode = t("Add");
  if (isEditMode()) mode = t("Edit");

  const fieldTypeOptions: Option[] = [
    { _id: "Text", name: t("Text") },
    { _id: "Number", name: t("Number") },
    { _id: "Sequence", name: t("Sequence") },
    { _id: "FormTemplate", name: t("FormTemplate") },
    { _id: "Glossary", name: t("Glossary") },
    { _id: "Domain", name: t("Domain") },
  ];

  switch (fieldTypeValue) {
    case "Sequence":
      labelRefElementId = t("Sequence");
      break;
    case "FormTemplate":
      labelRefElementId = t("Form");
      break;
    case "Glossary":
      labelRefElementId = t("Glossary");
      break;
  }

  return (
    <Loading loaded={form.state.loaded}>
      <h1>
        {mode} {t("CustomField")}
      </h1>
      <form onSubmit={handleSubmit}>
        {renderError("_general", form.state.errors)}
        {renderInput(
          "name",
          labelName,
          form.state.data,
          form.state.errors,
          InputType.text,
          false,
          "",
          "",
          0,
          true,
          undefined,
          form.handleChange,
        )}
        {renderSelect(
          "fieldType",
          labelFieldType,
          form.state.data,
          form.state.errors,
          fieldTypeOptions,
          form.handleSelectChange,
        )}
        {fieldTypeValue === "Domain" && (
          <>
            {renderInput(
              "required",
              labelRequired,
              form.state.data,
              form.state.errors,
              InputType.checkbox,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
            {renderInputNumber(
              "maxEntries",
              labelMaxEntries,
              form.state.data,
              form.state.errors,
              false,
              "",
              undefined,
              undefined,
              1,
              form.handleChange,
            )}
          </>
        )}
        {fieldTypeValue === "Glossary" && (
          <>
            {renderGlossaryPicker(
              true,
              "refElementId",
              labelRefElementId,
              form.state.data,
              form.state.errors,
              1,
              SystemGlossaries,
              form.handleGlossaryPickerChange,
            )}
            {renderInput(
              "required",
              labelRequired,
              form.state.data,
              form.state.errors,
              InputType.checkbox,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
            {renderInputNumber(
              "maxEntries",
              labelMaxEntries,
              form.state.data,
              form.state.errors,
              false,
              "",
              undefined,
              undefined,
              1,
              form.handleChange,
            )}
          </>
        )}
        {fieldTypeValue === "Sequence" && (
          <>
            {renderSequencePicker(
              true,
              "refElementId",
              labelRefElementId,
              form.state.data,
              form.state.errors,
              form.handlePickerChange,
            )}
          </>
        )}
        {fieldTypeValue === "Text" && (
          <>
            {renderInput(
              "multiLine",
              labelMultiLine,
              form.state.data,
              form.state.errors,
              InputType.checkbox,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
            {form.state.data.multiLine === true &&
              renderInputTextarea(
                true,
                "defaultValue",
                labelDefaultValue,
                form.state.data,
                form.state.errors,
                false,
                "",
                form.handleTextAreaChange,
              )}
            {form.state.data.multiLine === false &&
              renderInput(
                "defaultValue",
                labelDefaultValue,
                form.state.data,
                form.state.errors,
                InputType.text,
                false,
                "",
                "",
                0,
                true,
                undefined,
                form.handleChange,
              )}
          </>
        )}
        {fieldTypeValue === "Number" && (
          <>
            {renderInput(
              "required",
              labelRequired,
              form.state.data,
              form.state.errors,
              InputType.checkbox,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
            {renderInputNumber(
              "minValue",
              labelMinValue,
              form.state.data,
              form.state.errors,
              false,
              "",
              undefined,
              undefined,
              1,
              form.handleChange,
            )}
            {renderInputNumber(
              "maxValue",
              labelMaxValue,
              form.state.data,
              form.state.errors,
              false,
              "",
              undefined,
              undefined,
              1,
              form.handleChange,
            )}
            {renderInput(
              "step",
              labelStep,
              form.state.data,
              form.state.errors,
              InputType.number,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
            {renderInputNumber(
              "defaultValue",
              labelDefaultValue,
              form.state.data,
              form.state.errors,
              false,
              "",
              undefined,
              undefined,
              1,
              form.handleChange,
            )}
          </>
        )}
        {![
          "Domain",
          "Glossary",
          "Sequence",
          "FormTemplate",
          "Text",
          "Number",
        ].includes(fieldTypeValue) && (
          <>
            {renderInput(
              "defaultValue",
              labelDefaultValue,
              form.state.data,
              form.state.errors,
              InputType.text,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
            {renderInput(
              "minEntries",
              labelMinEntries,
              form.state.data,
              form.state.errors,
              InputType.number,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
            {renderInput(
              "maxEntries",
              labelMaxEntries,
              form.state.data,
              form.state.errors,
              InputType.number,
              false,
              "",
              "",
              0,
              true,
              undefined,
              form.handleChange,
            )}
          </>
        )}
        {isEditMode() && renderButton(labelApply, form.state.errors, "apply")}
        {renderButton(labelSave, form.state.errors, "save")}
      </form>
    </Loading>
  );
};

export default CustomFieldDetails;
