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 { GeneralIdRef, MakeGeneralIdRef } from "../../../utils/GeneralIdRef";
import glossariesService, {
  CustomFieldValue,
  Glossary,
  SystemGlossaries,
} from "./services/glossaryService";
import { CustomField } from "../customfields/services/customFieldsService";
import Loading from "../../../components/common/Loading";
import {
  renderError,
  renderButton,
  renderInput,
  renderCustomFields,
  renderCustomFieldsEditor,
} from "../../../components/common/formHelpers";
import { useFormWithGuard } from "../../../components/common/useFormRouter";

interface GlossariesDetailsProps {
  editMode?: boolean;
}

const GlossariesDetails: React.FC<GlossariesDetailsProps> = ({
  editMode = false,
}) => {
  const { glossaryId } = useParams<{ glossaryId: string }>();
  const { t } = useTranslation(Namespaces.Common);

  const labelName = t("Name");
  const labelChildCustomFieldDefinition = t("CustomFieldForChildEntries");

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

  const form = useFormWithGuard({
    loaded: false,
    data: {
      id: undefined,
      guid: undefined,
      name: "",
      parent: undefined,
      childCustomFieldDefinition: [],
    },
    errors: {},
    redirect: "",
  });

  form.schema = {
    id: Joi.optional(),
    guid: Joi.optional(),
    name: Joi.string().required().max(450).label(labelName),
    parent: Joi.optional(),
    childCustomFieldDefinition: Joi.optional().label(
      labelChildCustomFieldDefinition,
    ),
  };

  useEffect(() => {
    const loadData = async () => {
      const newData = { ...form.state.data };

      if (editMode) {
        const generalIdRef = MakeGeneralIdRef(BigInt(glossaryId!));
        try {
          const loadedData =
            await glossariesService.getGlossaryItem(generalIdRef);
          if (loadedData) {
            newData.name = loadedData.name;
            newData.parent = loadedData.parent;
            newData.id = loadedData.id;
            newData.guid = loadedData.guid;
            newData.childCustomFieldDefinition =
              loadedData.childCustomFieldDefinition ?? [];

            const parentGlossary = newData.parent as Glossary | undefined;

            form.setCustomFieldValues(
              newData,
              loadedData.customFieldValues,
              parentGlossary?.childCustomFieldDefinition ?? [],
            );
          }
        } catch (ex: any) {
          form.handleGeneralError(ex);
        }
      } else {
        const generalIdRef = MakeGeneralIdRef(BigInt(glossaryId!));
        try {
          const loadedData =
            await glossariesService.getGlossaryItem(generalIdRef);
          if (loadedData) {
            newData.parent = loadedData;
          }
        } catch (ex: any) {
          form.handleGeneralError(ex);
        }
      }

      form.setState({
        loaded: true,
        data: newData,
        customFields: (newData.parent as Glossary | undefined)
          ?.childCustomFieldDefinition,
      });
    };

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

  const handleAdd = (customfield: CustomField) => {
    const newData = { ...form.state.data };
    const childDefs =
      (newData.childCustomFieldDefinition as CustomField[]) ?? [];
    childDefs.push(customfield);
    newData.childCustomFieldDefinition = childDefs;

    form.setState({ data: newData });
  };

  const handleDelete = (fieldToDelete: CustomField) => {
    const newData = { ...form.state.data };

    if (fieldToDelete) {
      const childDefs =
        (newData.childCustomFieldDefinition as CustomField[]) ?? [];
      newData.childCustomFieldDefinition = childDefs.filter(
        (x) => x !== fieldToDelete,
      );
    }

    form.setState({ data: newData });
  };

  const doSubmit = async (buttonName: string) => {
    try {
      const { id, guid, name, parent, childCustomFieldDefinition } =
        form.state.data;
      const nameStr = typeof name === "string" ? name : "";
      const parentGlossary = parent as Glossary | undefined;
      const childDefs = (childCustomFieldDefinition as CustomField[]) ?? [];
      const idValue = typeof id === "bigint" ? id : undefined;
      const guidValue = typeof guid === "string" ? guid : undefined;

      const customfieldValues = form.CustomFieldValues();

      if (editMode) {
        const generalIdRef = MakeGeneralIdRef(idValue, guidValue);

        const response = await glossariesService.putGlossaryItem(
          generalIdRef,
          parentGlossary
            ? MakeGeneralIdRef(parentGlossary.id, parentGlossary.guid)
            : undefined,
          nameStr,
          childDefs,
          customfieldValues,
        );
        if (response) {
          toast.info(t("GlossaryItemEdited"));
        }
      } else {
        const generalIdRef = parentGlossary
          ? MakeGeneralIdRef(parentGlossary.id, parentGlossary.guid)
          : SystemGlossaries;
        const response = await glossariesService.postGlossaryItem(
          generalIdRef,
          nameStr,
          childDefs,
          customfieldValues,
        );
        if (response) {
          toast.info(t("NewGlossaryItemAdded"));
        }
      }

      const navigateId = parentGlossary ? parentGlossary.id.toString() : "";
      if (buttonName === "save")
        form.setState({ redirect: "/glossaries/" + navigateId });
      form.markAsSaved();
    } catch (ex: any) {
      form.handleGeneralError(ex);
    }
  };

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

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

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

  const parentGlossary = data.parent as Glossary | undefined;
  const handleCustomFieldChange = (
    e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
  ) => {
    if (e.target instanceof HTMLTextAreaElement) {
      form.handleTextAreaChange(e as React.ChangeEvent<HTMLTextAreaElement>);
    } else {
      form.handleCustomFieldChange(e as React.ChangeEvent<HTMLInputElement>);
    }
  };
  const getCustomFieldType = (
    field: CustomFieldValue,
    customFields: CustomField[],
  ) => {
    return form.getCustomFieldType(field as any, customFields);
  };
  const handleCustomFieldPickerChange = (
    name: string,
    value: GeneralIdRef | CustomFieldValue[],
  ) => {
    if (Array.isArray(value)) {
      form.handleGlossaryPickerChange(name, value);
    } else {
      form.handlePickerChange(name, value as GeneralIdRef);
    }
  };

  return (
    <Loading loaded={loaded}>
      <h1>
        {mode} {t("GlossaryItem")}
      </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,
        )}
        {renderCustomFields(
          parentGlossary?.childCustomFieldDefinition,
          form.state.data,
          form.state.errors,
          handleCustomFieldChange,
          handleCustomFieldPickerChange,
          getCustomFieldType,
        )}
        <hr />
        {renderCustomFieldsEditor(
          "childCustomFieldDefinition",
          labelChildCustomFieldDefinition,
          form.state.data,
          form.state.errors,
          (data.childCustomFieldDefinition as CustomField[]) ?? [],
          handleAdd,
          handleDelete,
        )}
        <br />
        {editMode && renderButton(labelApply, form.state.errors, "apply")}
        {renderButton(labelSave, form.state.errors, "save")}
      </form>
    </Loading>
  );
};

export default GlossariesDetails;
