// mobx
import { action, observable, runInAction, makeAutoObservable } from "mobx";
// services
import customAttributesService from "services/custom-attributes.service";
// types
import type { ICustomAttribute } from "types";

class CustomAttributesStore {
  objects: ICustomAttribute[] | null = null;
  objectAttributes: {
    [objectId: string]: { [objectAttributeId: string]: ICustomAttribute };
  } = {};
  // loaders
  fetchObjectsLoader = false;
  fetchObjectDetailsLoader = false;
  createObjectAttributeLoader = false;
  createAttributeOptionLoader = false;
  // errors
  attributesFetchError: any | null = null;
  error: any | null = null;
  rootStore: any | null = null;

  constructor(_rootStore: any | null = null) {
    makeAutoObservable(this, {
      objects: observable.ref,
      objectAttributes: observable.ref,
      fetchObjects: action,
      fetchObjectDetails: action,
      createObject: action,
      updateObject: action,
      deleteObject: action,
      createObjectAttribute: action,
      updateObjectAttribute: action,
      deleteObjectAttribute: action,
      createAttributeOption: action,
      updateAttributeOption: action,
      deleteAttributeOption: action,
    });

    this.rootStore = _rootStore;
  }

  fetchObjects = async (workspaceSlug: string, projectId: string) => {
    try {
      runInAction(() => {
        this.fetchObjectsLoader = true;
      });

      const response = await customAttributesService.getObjectsList(workspaceSlug, {
        project: projectId,
      });

      if (response) {
        runInAction(() => {
          this.objects = response;
          this.fetchObjectsLoader = false;
        });
      }
    } catch (error) {
      runInAction(() => {
        this.fetchObjectsLoader = false;
        this.attributesFetchError = error;
      });
    }
  };

  fetchObjectDetails = async (workspaceSlug: string, objectId: string) => {
    try {
      runInAction(() => {
        this.fetchObjectDetailsLoader = true;
      });

      const response = await customAttributesService.getPropertyDetails(workspaceSlug, objectId);

      const objectChildren: { [key: string]: ICustomAttribute } = response.children.reduce(
        (acc, child) => ({
          ...acc,
          [child.id]: child,
        }),
        {}
      );

      runInAction(() => {
        this.objectAttributes = {
          ...this.objectAttributes,
          [objectId]: objectChildren,
        };
        this.fetchObjectDetailsLoader = false;
      });

      return response;
    } catch (error) {
      runInAction(() => {
        this.fetchObjectDetailsLoader = false;
        this.error = error;
      });
    }
  };

  createObject = async (workspaceSlug: string, data: Partial<ICustomAttribute>) => {
    try {
      const response = await customAttributesService.createProperty(workspaceSlug, data);

      runInAction(() => {
        this.objects = [...(this.objects ?? []), response];
      });

      return response;
    } catch (error) {
      runInAction(() => {
        this.error = error;
      });
    }
  };

  updateObject = async (
    workspaceSlug: string,
    objectId: string,
    data: Partial<ICustomAttribute>
  ) => {
    try {
      const response = await customAttributesService.patchProperty(workspaceSlug, objectId, data);

      const newObjects = [...(this.objects ?? [])].map((object) =>
        object.id === objectId ? { ...object, ...response } : object
      );

      runInAction(() => {
        this.objects = newObjects;
      });

      return response;
    } catch (error) {
      runInAction(() => {
        this.error = error;
      });
    }
  };

  deleteObject = async (workspaceSlug: string, objectId: string) => {
    try {
      await customAttributesService.deleteProperty(workspaceSlug, objectId);

      const newObjects = this.objects?.filter((object) => object.id !== objectId);

      runInAction(() => {
        this.objects = [...(newObjects ?? [])];
      });
    } catch (error) {
      runInAction(() => {
        this.error = error;
      });
    }
  };

  createObjectAttribute = async (
    workspaceSlug: string,
    data: Partial<ICustomAttribute> & { parent: string }
  ) => {
    try {
      runInAction(() => {
        this.createObjectAttributeLoader = true;
      });

      const response = await customAttributesService.createProperty(workspaceSlug, data);

      runInAction(() => {
        this.objectAttributes = {
          ...this.objectAttributes,
          [data.parent]: {
            ...this.objectAttributes[data.parent],
            [response.id]: response,
          },
        };
        this.createObjectAttributeLoader = false;
      });

      return response;
    } catch (error) {
      runInAction(() => {
        this.error = error;
        this.createObjectAttributeLoader = false;
      });
    }
  };

  updateObjectAttribute = async (
    workspaceSlug: string,
    parentId: string,
    propertyId: string,
    data: Partial<ICustomAttribute>
  ) => {
    try {
      await customAttributesService.patchProperty(workspaceSlug, propertyId, data);

      const newObjects = this.objectAttributes[parentId];
      newObjects[propertyId] = {
        ...newObjects[propertyId],
        ...data,
      };

      runInAction(() => {
        this.objectAttributes = {
          ...this.objectAttributes,
          [parentId]: newObjects,
        };
      });
    } catch (error) {
      runInAction(() => {
        this.error = error;
      });
    }
  };

  deleteObjectAttribute = async (workspaceSlug: string, parentId: string, propertyId: string) => {
    try {
      await customAttributesService.deleteProperty(workspaceSlug, propertyId);

      const newObjects = this.objectAttributes[parentId];
      delete newObjects[propertyId];

      runInAction(() => {
        this.objectAttributes = {
          ...this.objectAttributes,
          [parentId]: newObjects,
        };
      });
    } catch (error) {
      runInAction(() => {
        this.error = error;
      });
    }
  };

  createAttributeOption = async (
    workspaceSlug: string,
    objectId: string,
    data: Partial<ICustomAttribute> & { parent: string }
  ) => {
    try {
      runInAction(() => {
        this.createAttributeOptionLoader = true;
      });

      const response = await customAttributesService.createProperty(workspaceSlug, data);

      runInAction(() => {
        this.objectAttributes = {
          ...this.objectAttributes,
          [objectId]: {
            ...this.objectAttributes[objectId],
            [data.parent]: {
              ...this.objectAttributes[objectId][data.parent],
              children: [...this.objectAttributes[objectId][data.parent].children, response],
            },
          },
        };
        this.createAttributeOptionLoader = false;
      });

      return response;
    } catch (error) {
      runInAction(() => {
        this.error = error;
        this.createAttributeOptionLoader = false;
      });
    }
  };

  updateAttributeOption = async (
    workspaceSlug: string,
    objectId: string,
    parentId: string,
    propertyId: string,
    data: Partial<ICustomAttribute>
  ) => {
    try {
      const response = await customAttributesService.patchProperty(workspaceSlug, propertyId, data);

      const newOptions = this.objectAttributes[objectId][parentId].children.map((option) => ({
        ...option,
        ...(option.id === propertyId ? response : {}),
      }));

      runInAction(() => {
        this.objectAttributes = {
          ...this.objectAttributes,
          [objectId]: {
            ...this.objectAttributes[objectId],
            [parentId]: {
              ...this.objectAttributes[objectId][parentId],
              children: newOptions,
            },
          },
        };
      });

      return response;
    } catch (error) {
      runInAction(() => {
        this.error = error;
      });
    }
  };

  deleteAttributeOption = async (
    workspaceSlug: string,
    objectId: string,
    parentId: string,
    propertyId: string
  ) => {
    const newOptions = this.objectAttributes[objectId][parentId].children.filter(
      (option) => option.id !== propertyId
    );

    try {
      runInAction(() => {
        this.objectAttributes = {
          ...this.objectAttributes,
          [objectId]: {
            ...this.objectAttributes[objectId],
            [parentId]: {
              ...this.objectAttributes[objectId][parentId],
              children: newOptions,
            },
          },
        };
      });

      await customAttributesService.deleteProperty(workspaceSlug, propertyId);
    } catch (error) {
      runInAction(() => {
        this.error = error;
      });

      this.fetchObjectDetails(workspaceSlug, objectId);
    }
  };
}

export default CustomAttributesStore;