import { CategoryDao } from "../dao/category.dao";
import { AttachmentService } from "../services/attachment.service";
import { LanguageService } from "../services/language.service";
import { CategoryTypeService } from "../services/categoryType.service";
import { sequelize } from "../models";
import { Common } from "../../utils/common";
import { AppError } from "../../utils/errors";
import { CATEGORY } from "../config/constants";
import { SeaQADao } from "../dao/seaQA.dao";


export class CategoryService {
  private accountId: number | null;
  private userId: number | null;
  private language: string;
  private scope: string[] | null;
  private config: userConfig | null;
  private categoryDao: CategoryDao;
  private categoryTypeService: CategoryTypeService;
  private languageService: LanguageService;
  private attachmentService: AttachmentService;
  private seaQADao: SeaQADao;
  constructor(

    private options: {
      language: string;
      scope: string[] | null;
      accountId: number | null;
      userId: number | null;
      config: userConfig | null;
    } = {
      language: process.env.DEFAULT_LANGUAGE_CODE!,
      scope: null,
      config: null,
      userId: null,
      accountId: null,
    },
  ) {
    this.language = options.language;
    this.scope = options.scope ?? [];
    this.config = options.config ?? null;
    this.userId = options.userId ?? null;
    this.accountId = options.accountId ?? null;
    this.categoryDao = new CategoryDao({
      userId: this.userId,
      accountId: this.accountId,
      language: this.language,
      scope: this.scope,
      config: this.config,
    });
    this.categoryTypeService = new CategoryTypeService({
      userId: this.userId,
      accountId: this.accountId,
      language: this.language,
      scope: this.scope,
      config: this.config,
    });
    this.languageService = new LanguageService({
      userId: this.userId,
      accountId: this.accountId,
      language: this.language,
      scope: this.scope,
      config: this.config,
    });
    this.attachmentService = new AttachmentService({
      userId: this.userId,
      accountId: this.accountId,
      language: this.language,
      scope: this.scope,
      config: this.config,
    });
    this.seaQADao = new SeaQADao({
      userId: this.userId,
      accountId: this.accountId,
      language: this.language,
      scope: this.scope,
      config: this.config,
    });
  }

  // create a new Category
  createCategory = async (
    data: CategoryCreateServiceInput,
  ): Promise<CategoryObjectInteface> => {
    const transaction = await sequelize.transaction();
    try {
      const {
        categoryTypeCode,
        imageId,
        parentId,
        status,
        refLink,
        name,
        description,
      } = data;
      let categoryObj: CategoryObject = {
        imageId,
        parentId,
        refLink,
        status: status ?? CATEGORY.STATUS.ACTIVE,
      };
      let categoryContentObj: CategoryContentObject = { name, description };
      let asSuperAdmin: boolean = false;
      if (
        ["permission"].includes(categoryTypeCode) &&
        this.scope &&
        !this.scope.includes("superadmin")
      ) {
        throw new AppError(401, "PERMISSION_DENIED", {});
      } else if (
        ["permission"].includes(categoryTypeCode) &&
        this.scope &&
        this.scope.includes("superadmin")
      ) {
        asSuperAdmin = true;
      }
      const getCategoryTypeIdInput: CategoryTypeGetIdServiceInput = {
        code: categoryTypeCode,
      };
      const categoryTypeId = await this.categoryTypeService.getCategoryTypeId(
        getCategoryTypeIdInput,
      );
      categoryObj.categoryTypeId = categoryTypeId;
      const languages: LanguageInfo =
        await this.languageService.getRequestedAndDefaultLanguage();
      const code = `${categoryTypeCode}-${Common.slugify(categoryContentObj.name)}`;
      categoryObj.code = code;
      if (code) {
        if (["permission"].includes(code)) {
          throw new AppError(400, "RESERVED_SYSTEM_CATEGORY", {
            name: "RESERVED_SYSTEM_CATEGORY",
          });
        }
      }
      categoryContentObj.descriptionText = (categoryContentObj.description
        ? await Common.convertHtmlToText(
            categoryContentObj.description.toString(),
          )
        : null) as unknown as Text;
      const doesCategoryExistInput: CategoryDoExistsByCodeDaoInput = { code };
      const exists = await this.categoryDao.doExistsByCode(
        doesCategoryExistInput,
        { transaction },
      );
      if (categoryObj.parentId) {
        // check if parent category exists and have same type
        const parentExistsInput: CategoryDoExistsByIdDaoInput = {
          id: categoryObj.parentId,
        };
        const parentExists = await this.categoryDao.doExistsById(
          parentExistsInput,
          { transaction },
        );
        if (!parentExists || parentExists.categoryTypeId != categoryTypeId) {
          throw new AppError(404, "PARENT_CATEGORY_NOT_FOUND", {
            parentId: "PARENT_CATEGORY_NOT_FOUND",
          });
        }
      }
      if (categoryObj.imageId) {
        // check if parent category exists and have same type
        const imageExists = await this.attachmentService.verifyFile([
          categoryObj.imageId,
        ]);
        if (imageExists.length != 1) {
          throw new AppError(404, "IMAGE_NOT_FOUND", {
            imageId: "IMAGE_NOT_FOUND",
          });
        }
      }
      // check if category exists
      const getCategoryTypeByIdInput: CategoryTypeGetByIdServiceInput = {
        id: categoryTypeId,
      };
      let categoryTypeExists = await this.categoryTypeService.getById(
        getCategoryTypeByIdInput,
      );
      if (categoryTypeExists) {
        if (!exists) {
          const createCategoryInput: CategoryCreateDaoInput = {
            categoryObj,
            categoryContentObj,
            asSuperAdmin,
            languages,
          };
          let categoryIdentifier: number = await this.categoryDao.create(
            createCategoryInput,
            { transaction },
          );
          const setSortOrderInput: CategorySetSortOrderDaoInput = {
            id: categoryIdentifier,
            categoryTypeId,
            asSuperAdmin,
          };
          await this.categoryDao.setSortOrder(setSortOrderInput, {
            transaction,
          });
          const getCategoryByIdInput: CategoryGetByIdDaoInput = {
            id: categoryIdentifier,
            fullInfo: true,
          };
          let category = await this.categoryDao.getCategoryById(
            getCategoryByIdInput,
            { transaction },
          );

          if (categoryTypeCode === 'seaqa' && data.seaQAApplicability) {
            await this.seaQADao.updateApplicability(
              categoryIdentifier,
              [data.seaQAApplicability.applicableRankId],
              [data.seaQAApplicability.applicableShipTypeId],
              data.seaQAApplicability.applicableTo,
              transaction
            );
          }

          await transaction.commit();
          return category;

        }
        throw new AppError(400, "CATEGORY_ALREADY_EXISTS", {
          name: "CATEGORY_ALREADY_EXISTS",
        });
      } else {
        throw new AppError(404, "CATEGORY_TYPE_NOT_FOUND", {
          categoryTypeId: "CATEGORY_TYPE_NOT_FOUND",
        });
      }
    } catch (err) {
      await transaction.rollback();
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // update the existing category
  updateCategory = async (
    data: CategoryUpdateServiceInput,
  ): Promise<CategoryObjectInteface> => {
    const transaction = await sequelize.transaction();
    try {
      const {
        id,
        categoryTypeCode,
        imageId,
        parentId,
        status,
        refLink,
        name,
        description,
      } = data;
      let categoryObj: CategoryObject = {
        imageId,
        parentId,
        refLink,
        status: status ?? CATEGORY.STATUS.ACTIVE,
      };
      let categoryContentObj: CategoryContentObject = { name, description };
      if (
        ["permission"].includes(categoryTypeCode) &&
        this.scope &&
        !this.scope.includes("superadmin")
      ) {
        throw new AppError(401, "PERMISSION_DENIED", {});
      }
      const getCategoryTypeIdInput: CategoryTypeGetIdServiceInput = {
        code: categoryTypeCode,
      };
      const categoryTypeId = await this.categoryTypeService.getCategoryTypeId(
        getCategoryTypeIdInput,
      );
      const languages: LanguageInfo =
        await this.languageService.getRequestedLanguage();
      const code =
        languages.requested.code == process.env.DEFAULT_LANGUAGE_CODE
          ? `${categoryTypeCode}-${Common.slugify(categoryContentObj.name)}`
          : null;
      if (code) {
        categoryObj.code = code;
        if (["permission"].includes(code)) {
          throw new AppError(400, "RESERVED_SYSTEM_CATEGORY", {
            name: "RESERVED_SYSTEM_CATEGORY",
          });
        }
      }
      categoryContentObj.descriptionText = (categoryContentObj.description
        ? await Common.convertHtmlToText(
            categoryContentObj.description.toString(),
          )
        : null) as unknown as Text;
      const doesCategoryExistInput: CategoryDoExistsByIdDaoInput = { id };
      const exists = await this.categoryDao.doExistsById(
        doesCategoryExistInput,
        { transaction },
      );
      const getCategoryTypeByIdInput: CategoryTypeGetByIdServiceInput = {
        id: categoryTypeId,
      };
      let categoryTypeExists = await this.categoryTypeService.getById(
        getCategoryTypeByIdInput,
      );
      if (categoryTypeExists) {
        if (categoryObj.parentId) {
          // check if parent category exists and have same type and its not creating the infinite loop with child
          const parentExistsInput: CategoryDoExistsByIdDaoInput = {
            id: categoryObj.parentId,
          };
          const parentExists = await this.categoryDao.doExistsById(
            parentExistsInput,
            { transaction },
          );
          if (!parentExists || parentExists.categoryTypeId != categoryTypeId) {
            throw new AppError(404, "PARENT_CATEGORY_NOT_FOUND", {
              parentId: "PARENT_CATEGORY_NOT_FOUND",
            });
          } else if (parentExists.categoryTypeId != categoryTypeId) {
            throw new AppError(
              404,
              "PARENT_AND_CATEGORY_ARE_NOT_OF_SAME_TYPE",
              { categoryTypeId: "PARENT_AND_CATEGORY_ARE_NOT_OF_SAME_TYPE" },
            );
          } else if (parentExists.categoryTypeId == categoryTypeId) {
            if (parentExists.id == id) {
              throw new AppError(400, "CATGORY_CANNOT_BE_ASSIGNED_AS_PARENT", {
                parentId: "CATGORY_CANNOT_BE_ASSIGNED_AS_PARENT",
              });
            }
            const ifChildIsParentInput: CategoryIfChildIsParentDaoInput = {
              categoryId: id,
              parentId: categoryObj.parentId,
            };
            let ifChildIsParent = await this.categoryDao.ifChildIsParent(
              ifChildIsParentInput,
              { transaction },
            );
            if (ifChildIsParent) {
              throw new AppError(
                400,
                "CHILD_CATEGORY_CANNOT_BE_ASSIGNED_AS_PARENT",
                { parentId: "CHILD_CATEGORY_CANNOT_BE_ASSIGNED_AS_PARENT" },
              );
            }
          }
        }

        if (categoryObj.imageId) {
          // check if parent category exists and have same type
          const imageExists = await this.attachmentService.verifyFile([
            categoryObj.imageId,
          ]);
          if (imageExists.length != 1) {
            throw new AppError(404, "IMAGE_NOT_FOUND", {
              imageId: "IMAGE_NOT_FOUND",
            });
          }
        }
        if (exists) {
          if (code) {
            const codeInUseInput: CategoryDoExistsByCodeDaoInput = {
              code,
              excludeId: id,
            };
            let codeInUse = await this.categoryDao.doExistsByCode(
              codeInUseInput,
              { transaction },
            );
            if (codeInUse) {
              throw new AppError(400, "CATEGORY_ALREADY_EXISTS", {
                name: "CATEGORY_ALREADY_EXISTS",
              });
            }
          }
          const updateCategoryInput: CategoryUpdateDaoInput = {
            id,
            categoryObj,
            categoryContentObj,
            languages,
          };
          await this.categoryDao.update(updateCategoryInput, { transaction });
          const getCategoryByIdInput: CategoryGetByIdDaoInput = { id };
          let category = await this.categoryDao.getCategoryById(
            getCategoryByIdInput,
            { transaction },
          );

          if (categoryTypeCode === 'seaqa' && data.seaQAApplicability) {
            await this.seaQADao.updateApplicability(
              id,
              [data.seaQAApplicability.applicableRankId],
              [data.seaQAApplicability.applicableShipTypeId],
              data.seaQAApplicability.applicableTo,
              transaction
            );
          }

          await transaction.commit();
          return category;

        }
        throw new AppError(404, "CATEGORY_NOT_FOUND", {
          name: "CATEGORY_NOT_FOUND",
        });
      } else {
        throw new AppError(400, "CATEGORY_TYPE_NOT_FOUND", {
          categoryTypeId: "CATEGORY_TYPE_NOT_FOUND",
        });
      }
    } catch (err) {
      await transaction.rollback();
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // delete the existing category
  deleteCategory = async (
    data: CategoryDeleteServiceInput,
  ): Promise<CategoryObjectInteface> => {
    const transaction = await sequelize.transaction();
    try {
      const { id, categoryTypeCode } = data;
      let asSuperAdmin = false;
      if (
        ["permission"].includes(categoryTypeCode) &&
        this.scope &&
        !this.scope.includes("superadmin")
      ) {
        throw new AppError(401, "PERMISSION_DENIED", {});
      } else if (
        ["permission"].includes(categoryTypeCode) &&
        this.scope &&
        this.scope.includes("superadmin")
      ) {
        asSuperAdmin = true;
      }
      const doesCategoryExistInput: CategoryDoExistsByIdDaoInput = { id };
      const exists = await this.categoryDao.doExistsById(
        doesCategoryExistInput,
        { transaction },
      );
      if (exists) {
        // check if category has active child categories
        const hasChildInput: CategoryHasChildDaoInput = { id };
        if (
          !(await this.categoryDao.hasChild(hasChildInput, { transaction }))
        ) {
          const deleteCategoryInput: CategoryDeleteDaoInput = {
            id,
            asSuperAdmin,
          };
          await this.categoryDao.delete(deleteCategoryInput, { transaction });
          const getCategoryByIdInput: CategoryGetByIdDaoInput = {
            id,
            fullInfo: true,
            paranoid: false,
          };
          let category = await this.categoryDao.getCategoryById(
            getCategoryByIdInput,
            { transaction },
          );
          await transaction.commit();
          return category;
        } else {
          throw new AppError(400, "CATEGORY_HAS_ACTIVE_CHILD_CATEGORIES", {
            id: "CATEGORY_HAS_ACTIVE_CHILD_CATEGORIES",
          });
        }
      }
      throw new AppError(404, "CATEGORY_NOT_FOUND", {
        name: "CATEGORY_NOT_FOUND",
      });
    } catch (err) {
      await transaction.rollback();
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // get category by id
  getById = async (
    data: CategoryGetByIdServiceInput,
  ): Promise<CategoryObjectInteface> => {
    try {
      const getCategoryByIdInput: CategoryGetByIdDaoInput = {
        id: data.id,
        fullInfo: data.fullInfo ?? true,
      };
      let category =
        await this.categoryDao.getCategoryById(getCategoryByIdInput);
      if (category) {
        return category;
      }
      throw new AppError(404, "CATEGORY_NOT_FOUND", {
        name: "CATEGORY_NOT_FOUND",
      });
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // get category by code
  getByCode = async (
    data: CategoryGetByCodeServiceInput,
  ): Promise<CategoryObjectInteface> => {
    try {
      const getCategoryByCodeInput: CategoryGetByCodeDaoInput = {
        code: data.code,
        fullInfo: data.fullInfo ?? true,
      };
      let category = await this.categoryDao.getCategoryByCode(
        getCategoryByCodeInput,
      );
      if (category) {
        return category;
      }
      throw new AppError(404, "CATEGORY_NOT_FOUND", {
        name: "CATEGORY_NOT_FOUND",
      });
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // update category status
  updateStatus = async (
    data: CategoryUpdateStatusServiceInput,
  ): Promise<CategoryObjectInteface> => {
    try {
      const getCategoryByIdInput: CategoryGetByIdDaoInput = { id: data.id };
      let category =
        await this.categoryDao.getCategoryById(getCategoryByIdInput);
      if (category) {
        const updateCategoryStatusInput: CategoryUpdateStatusDaoInput = {
          id: data.id,
          status: data.status,
        };
        await this.categoryDao.updateStatus(updateCategoryStatusInput);
        let category = await this.getById(getCategoryByIdInput);
        return category;
      } else {
        throw new AppError(404, "CATEGORY_NOT_FOUND", {
          id: "CATEGORY_NOT_FOUND",
        });
      }
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // list categories
  getCategories = async (
    data: CategoryGetCategoriesServiceInput,
  ): Promise<CategoryPaginatedList> => {
    try {
      const { categoryTypeCode, listRequest } = data;
      const { page, perPage } = listRequest;
      const getCategoryTypeIdInput: CategoryTypeGetIdServiceInput = {
        code: categoryTypeCode as string,
      };
      const categoryTypeId = categoryTypeCode
        ? await this.categoryTypeService.getCategoryTypeId(
            getCategoryTypeIdInput,
          )
        : null;
      const getCategoryListInput: CategoryGetCategoryListDaoInput = {
        categoryTypeId,
        listRequest,
      };
      let categories: CategoryPaginatedData =
        await this.categoryDao.getCategoryList(getCategoryListInput);
      if (categories) {
        let totalPages = Common.getTotalPages(categories.count, perPage);
        return {
          data: categories.rows,
          page: page,
          perPage: perPage,
          totalRecords: categories.count,
          totalPages: totalPages,
        } as unknown as CategoryPaginatedList;
      }
      throw new AppError(400, "CATEGORY_LIST_ERROR_OUT", {});
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // list all categories
  getAllCategories = async (
    data: CategoryGetAllCategoriesServiceInput,
  ): Promise<CategoryObjectSummaryInteface[]> => {
    try {
      const { categoryTypeCode, listRequest } = data;
      const getCategoryTypeIdInput: CategoryTypeGetIdServiceInput = {
        code: categoryTypeCode as string,
      };
      const categoryTypeId = categoryTypeCode
        ? await this.categoryTypeService.getCategoryTypeId(
            getCategoryTypeIdInput,
          )
        : null;
      const getAllCategoriesInput: CategoryGetAllCategoriesDaoInput = {
        categoryTypeId,
        listRequest,
      };
      let categories = await this.categoryDao.getAllCategories(
        getAllCategoriesInput,
      );
      if (categories) {
        return categories as unknown as CategoryObjectSummaryInteface[];
      }
      throw new AppError(400, "CATEGORY_TYPE_LIST_ERROR_OUT", {});
    } catch (err) {
      console.log(err);
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // list category revisions
  getCategoryRevisions = async (
    data: CategoryGetCategoryRevisionsServiceInput,
  ): Promise<CategoryPaginatedList> => {
    try {
      const { id, listRequest } = data;
      const { page, perPage } = listRequest;
      const getCategoryRevisionListInput: CategoryGetCategoryRevisionListDaoInput =
        { id, listRequest };
      let categories = await this.categoryDao.getCategoryRevisionList(
        getCategoryRevisionListInput,
      );
      if (categories) {
        let totalPages = Common.getTotalPages(categories.count, perPage);
        return {
          data: categories.rows,
          page: page,
          perPage: perPage,
          totalRecords: categories.count,
          totalPages: totalPages,
        } as unknown as CategoryPaginatedList;
      }
      throw new AppError(400, "CATEGORY_REVISION_LIST_ERROR_OUT", {});
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // Restore revision
  restoreRevision = async (
    data: CategoryRestoreRevisionServiceInput,
  ): Promise<CategoryObjectInteface> => {
    const transaction = await sequelize.transaction();
    try {
      const restoreRevisionInput: CategoryRestoreRevisionDaoInput = {
        id: data.id,
      };
      let restoredEntiryId: number = await this.categoryDao.restoreRevision(
        restoreRevisionInput,
        { transaction },
      );
      if (restoredEntiryId) {
        const getCategoryByIdInput: CategoryGetByIdDaoInput = {
          id: restoredEntiryId,
        };
        let category: CategoryObjectInteface =
          await this.categoryDao.getCategoryById(getCategoryByIdInput, {
            transaction,
          });
        await transaction.commit();
        return category;
      }
      throw new AppError(400, "CATEGORY_RESTORE_REVISION_ERROR_OUT", {});
    } catch (err) {
      await transaction.rollback();
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // get category tree
  getCategoryTree = async (
    data: CategoryGetCategoryTreeServiceInput,
  ): Promise<CategoryTree[]> => {
    try {
      const { categoryTypeCode } = data;
      const parentId = data.code
        ? await this.getCategoryId({ code: data.code })
        : null;
      const getCategoryTypeIdInput: CategoryTypeGetIdServiceInput = {
        code: categoryTypeCode,
      };
      const categoryTypeId = await this.categoryTypeService.getCategoryTypeId(
        getCategoryTypeIdInput,
      );
      const getCategoryTreeStructureInput: CategoryGetCategoryTreeStructureDaoInput =
        { categoryTypeId, parentId };
      let categoryTree: CategoryTree[] =
        await this.categoryDao.getCategoryTreeStructure(
          getCategoryTreeStructureInput,
        );
      if (categoryTree) {
        return categoryTree;
      }
      throw new AppError(400, "CATEGORY_RESTORE_REVISION_ERROR_OUT", {});
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // verify category
  verifyCategoryId = async (
    data: CategoryVerifyCategoryIdServiceInput,
  ): Promise<boolean> => {
    try {
      const verifyCategoryIdInput: CategoryVerifyCategoryIdDaoInput = {
        categoryId: data.categoryId,
        categoryTypeCode: data.categoryTypeCode,
      };
      return await this.categoryDao.verifyCategoryId(verifyCategoryIdInput);
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // get category id from code
  getCategoryId = async (
    data: CategoryGetCategoryIdServiceInput,
  ): Promise<number> => {
    try {
      const getCategoryIdInput: CategoryGetIdFromCodeDaoInput = {
        code: data.code,
      };
      let categoryId = await this.categoryDao.getIdFromCode(getCategoryIdInput);
      return categoryId;
    } catch (err) {
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };

  // set category sort order
  setSortOrder = async (
    data: CategorySetSortOrderServiceInput,
  ): Promise<boolean> => {
    const transaction = await sequelize.transaction();
    try {
      const { id, categoryTypeCode, before, after } = data;
      let asSuperAdmin: boolean = false;
      if (
        ["permission"].includes(categoryTypeCode) &&
        this.scope &&
        !this.scope.includes("superadmin")
      ) {
        throw new AppError(401, "PERMISSION_DENIED", {});
      } else if (
        ["permission"].includes(categoryTypeCode) &&
        this.scope &&
        this.scope.includes("superadmin")
      ) {
        asSuperAdmin = true;
      }
      const getCategoryTypeIdInput: CategoryTypeGetIdServiceInput = {
        code: categoryTypeCode as string,
      };
      const categoryTypeId = categoryTypeCode
        ? await this.categoryTypeService.getCategoryTypeId(
            getCategoryTypeIdInput,
          )
        : null;
      const setSortOrderInput: CategorySetSortOrderDaoInput = {
        id,
        categoryTypeId,
        before,
        after,
        asSuperAdmin,
      };
      let sortOrder: boolean = await this.categoryDao.setSortOrder(
        setSortOrderInput,
        { transaction },
      );
      await transaction.commit();
      return sortOrder;
    } catch (err) {
      await transaction.rollback();
      if (err instanceof AppError) {
        throw err;
      }
      throw new AppError(500, "SOMETHING_WENT_WRONG_IN_SERVICE", err);
    }
  };
}
