import Joi from "joi";
import React, { useEffect, useRef, useState, useCallback } 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 { useForm } from "../../../components/common/useForm";
import { InputType } from "../../../components/common/Input";
import Loading from "../../../components/common/Loading";
import TemplateFiller, {
  TemplateFillerHandle,
} from "../../../components/common/TemplateFiller";
import { GeneralIdRef, MakeGeneralIdRef } from "../../../utils/GeneralIdRef";
import {
  CustomFieldValue,
  PrintSpecificationsGlossary,
} from "../glossary/services/glossaryService";
import specificationService from "./services/specificationService";
import {
  renderInput,
  renderButton,
  renderError,
  renderGlossaryPicker,
} from "../../../components/common/formHelpers";

interface SpecificationsDetailsProps {
  editMode?: boolean;
}

const SpecificationsDetails: React.FC<SpecificationsDetailsProps> = ({
  editMode = false,
}) => {
  const { organisationId, siteId, specificationId } = useParams<{
    organisationId: string;
    siteId: string;
    specificationId?: string;
  }>();
  const { t } = useTranslation<typeof Namespaces.Common>();
  const TemplateFillerRef = useRef<TemplateFillerHandle>(null);

  const labelName = t("Name");
  const labelPrintSpecification = t("PrintSpecification");
  const labelSigmaId = t("SigmaId");
  const labelApply = t("Save");
  const labelSave = t("SaveAndClose");

  const form = useForm({
    loaded: false,
    data: {
      name: "",
      sigmaId: null as bigint | null,
      printSpecifications: [] as CustomFieldValue[],
      formInstanceId: undefined,
    },
    errors: {},
    redirect: "",
  });

  const [formTemplate, setFormTemplate] = useState<GeneralIdRef | undefined>(
    undefined,
  );
  const [hasErrors, setHasErrors] = useState(false);

  form.schema = {
    name: Joi.string().required().max(450).label(labelName),
    printSpecifications: Joi.optional(),
    formInstanceId: Joi.optional(),
    sigmaId: Joi.number().allow(null).label(labelSigmaId),
  };

  const isEditMode = () => editMode;

  const loadFormTemplate = useCallback(
    async (printSpecifications?: GeneralIdRef) => {
      const { data } = form.state;
      const printSpecArray =
        (data.printSpecifications as CustomFieldValue[]) || [];

      if (printSpecArray && printSpecArray.length > 0) {
        if (
          ((printSpecArray[0] as CustomFieldValue).value as GeneralIdRef).id ===
          BigInt(0)
        ) {
          data.printSpecifications = undefined;
        }
      }

      let template;
      if (!printSpecArray || printSpecArray.length === 0) {
        template = undefined;
      } else {
        template = await specificationService.GetTemplateForPrintSpec(
          (printSpecArray[0] as CustomFieldValue).value as GeneralIdRef,
        );
      }
      if (template) {
        setFormTemplate(template);
        form.setState({ loaded: true });
      } else {
        form.setState({ loaded: false });
      }
    },
    [form],
  );

  const handleValidationChanged = useCallback(() => {
    const templateFiller = TemplateFillerRef.current;
    let templateErrors: boolean = false;
    if (templateFiller) templateErrors = templateFiller.hasValidationErrors();
    setHasErrors(templateErrors);
  }, []);

  useEffect(() => {
    const loadData = async () => {
      if (specificationId !== undefined) {
        try {
          const loadedData = await specificationService.GetSSpecification(
            BigInt(specificationId),
          );
          if (loadedData) {
            const newData = { ...form.state.data };
            newData.name = loadedData.name;
            newData.formInstanceId = loadedData.formInstanceId;
            form.setState({ loaded: true, data: newData });
          } else {
            form.setState({ loaded: false });
          }
        } catch (ex: any) {
          form.handleGeneralError(ex);
        }
      }

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

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

  useEffect(() => {
    const { printSpecifications } = form.state.data;
    const printSpecArray = (printSpecifications as CustomFieldValue[]) || [];
    if (printSpecArray && printSpecArray.length > 0) {
      loadFormTemplate(
        (printSpecArray[0] as CustomFieldValue).value as GeneralIdRef,
      );
    }
  }, [form.state.data.printSpecifications, loadFormTemplate, form]);

  const doSubmit = async (buttonName: string) => {
    try {
      const { name, formInstanceId, sigmaId } = form.state.data;
      const nameStr = typeof name === "string" ? name : "";
      const sigmaIdValue =
        sigmaId === null || sigmaId === undefined || sigmaId === ""
          ? null
          : typeof sigmaId === "bigint"
            ? sigmaId
            : BigInt(sigmaId as string | number);
      const siteIdGeneralIdRef = MakeGeneralIdRef(
        siteId ? BigInt(siteId) : undefined,
      );
      const templateFiller = TemplateFillerRef.current!;

      if (isEditMode()) {
        await templateFiller.Save();
        const specificationIdGeneralIdRef = MakeGeneralIdRef(
          specificationId ? BigInt(specificationId) : undefined,
        );
        const response = await specificationService.PutSpecification(
          specificationIdGeneralIdRef,
          siteIdGeneralIdRef,
          nameStr,
          formInstanceId as GeneralIdRef,
          sigmaIdValue,
        );
        if (response) {
          toast.info(t("SpecificationsEdited"));
        }
      } else {
        const newFormInstanceId = await templateFiller.Save();
        if (!newFormInstanceId) {
          toast.error(t("FailedToSaveFormInstance"));
          return;
        }

        const response = await specificationService.PostSpecification(
          siteIdGeneralIdRef,
          nameStr,
          newFormInstanceId!,
          sigmaIdValue,
        );
        if (response) {
          toast.info(t("NewSpecificationsAdded"));
        }
      }

      if (buttonName === "save") {
        form.setState({
          redirect: "/Specifications/" + organisationId + "/" + siteId,
        });
      }
    } catch (ex: any) {
      form.handleGeneralError(ex);
    }
  };

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

  const { loaded, redirect } = form.state;
  if (redirect && redirect !== "") return <Navigate to={redirect} />;

  const mode = isEditMode() ? t("Edit") : t("Add");

  return (
    <Loading loaded={loaded}>
      <h1>
        {mode} {t("Specifications")}
      </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,
        )}
        {renderInput(
          "sigmaId",
          labelSigmaId,
          form.state.data,
          form.state.errors,
          InputType.number,
          false,
          "",
          "",
          0,
          false,
          undefined,
          form.handleChange,
        )}
        {!isEditMode() &&
          renderGlossaryPicker(
            true,
            "printSpecifications",
            labelPrintSpecification,
            form.state.data,
            form.state.errors,
            1,
            PrintSpecificationsGlossary,
            form.handleGlossaryPickerChange,
          )}

        <TemplateFiller
          templateId={formTemplate}
          formInstanceId={
            form.state.data.formInstanceId as GeneralIdRef | undefined
          }
          ref={TemplateFillerRef}
          onValidationChanged={handleValidationChanged}
        />

        <br />
        {isEditMode() &&
          renderButton(
            labelApply,
            form.state.errors,
            "apply",
            undefined,
            undefined,
            Object.keys(form.state.errors).length === 0 && !hasErrors,
            undefined,
            true,
          )}
        {renderButton(
          labelSave,
          form.state.errors,
          "save",
          undefined,
          undefined,
          Object.keys(form.state.errors).length === 0 && !hasErrors,
          undefined,
          true,
        )}
      </form>
    </Loading>
  );
};

export default SpecificationsDetails;
