import { RoleKeycloak } from "./../entities/RoleKeycloak";
import { ProfileEmployee } from "./../entities/ProfileEmployee";
import { EmployeePosition } from "./../entities/EmployeePosition";
import { EmployeePosMaster } from "./../entities/EmployeePosMaster";
import { Position } from "./../entities/Position";
import { ProfileSalaryHistory } from "./../entities/ProfileSalaryHistory";
import { ProfileSalary } from "./../entities/ProfileSalary";
import {
  Controller,
  Get,
  Post,
  Put,
  Route,
  Security,
  Tags,
  Body,
  Path,
  Request,
  Response,
} from "tsoa";
import { CreateOrgRevision, OrgRevision } from "../entities/OrgRevision";
import { AppDataSource } from "../database/data-source";
import HttpSuccess from "../interfaces/http-success";
import { OrgChild1 } from "../entities/OrgChild1";
import HttpError from "../interfaces/http-error";
import HttpStatusCode from "../interfaces/http-status";
import { In, IsNull, Not, Like } from "typeorm";
import { OrgRoot } from "../entities/OrgRoot";
import { OrgChild2 } from "../entities/OrgChild2";
import { OrgChild3 } from "../entities/OrgChild3";
import { OrgChild4 } from "../entities/OrgChild4";
import { PosMaster } from "../entities/PosMaster";
import { Profile } from "../entities/Profile";
import { RequestWithUser } from "../middlewares/user";
import permission from "../interfaces/permission";
import {
  checkQueueInProgress,
  resolveNodeId,
  resolveNodeLevel,
  setLogDataDiff,
} from "../interfaces/utils";
import { sendToQueueOrg, sendToQueueOrgDraft } from "../services/rabbitmq";
import { PosType } from "../entities/PosType";
import { PosLevel } from "../entities/PosLevel";
import { PermissionOrg } from "../entities/PermissionOrg";
import {
  deleteUser,
  getToken,
  createUser,
  getUserByUsername,
  getRoles,
  addUserRoles,
  updateUserAttributes,
} from "../keycloak";
import {
  getPositionCountsAggregated,
  getPositionCount,
  PositionCountsByNode,
} from "../services/OrganizationService";
import {
  BatchSavePosMasterHistoryOfficer,
  CreatePosMasterHistoryEmployee,
  CreatePosMasterHistoryOfficer,
} from "../services/PositionService";
import { orgStructureCache } from "../utils/OrgStructureCache";
import { OrgIdMapping, AllOrgMappings, SavePosMasterHistory } from "../interfaces/OrgMapping";
import { OrgPermissionData, NodeLevel } from "../interfaces/OrgTypes";
import {
  formatPosMaster,
  generateLabelName,
  filterPosMasters,
  getPosMasterNo,
  getOrgFullName,
} from "../utils/org-formatting";

@Route("api/v1/org")
@Tags("Organization")
@Security("bearerAuth")
@Response(
  HttpStatusCode.INTERNAL_SERVER_ERROR,
  "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
)
export class OrganizationController extends Controller {
  private orgRevisionRepository = AppDataSource.getRepository(OrgRevision);
  private orgRootRepository = AppDataSource.getRepository(OrgRoot);
  private child1Repository = AppDataSource.getRepository(OrgChild1);
  private child2Repository = AppDataSource.getRepository(OrgChild2);
  private child3Repository = AppDataSource.getRepository(OrgChild3);
  private child4Repository = AppDataSource.getRepository(OrgChild4);
  private posMasterRepository = AppDataSource.getRepository(PosMaster);
  private profileRepo = AppDataSource.getRepository(Profile);
  private posTypeRepository = AppDataSource.getRepository(PosType);
  private posLevelRepository = AppDataSource.getRepository(PosLevel);
  private permissionOrgRepository = AppDataSource.getRepository(PermissionOrg);
  private profileSalaryRepository = AppDataSource.getRepository(ProfileSalary);
  private salaryHistoryRepo = AppDataSource.getRepository(ProfileSalaryHistory);
  private positionRepository = AppDataSource.getRepository(Position);

  private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee);
  private employeePosMasterRepository = AppDataSource.getRepository(EmployeePosMaster);
  private employeePositionRepository = AppDataSource.getRepository(EmployeePosition);
  private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak);

  /**
   * API ล้างข้อมูล
   *
   * @summary ล้างข้อมูล
   *
   */
  @Get("clear-db")
  async ClearDb() {
    return new HttpSuccess();
  }

  /**
   * API รายการประวัติโครงสร้าง
   *
   * @summary ORG_020 - รายการประวัติโครงสร้าง #21
   *
   */
  @Get("history")
  async GetHistory() {
    const orgRevision = await this.orgRevisionRepository.find({
      select: ["id", "orgRevisionName", "orgRevisionIsCurrent", "createdAt", "orgRevisionIsDraft"],
      order: { createdAt: "DESC" },
    });
    // if (!orgRevision) {
    //   return new HttpSuccess([]);
    // }
    const mapOrgRevisions = orgRevision.map((revision) => ({
      orgRevisionId: revision.id,
      orgRevisionName: revision.orgRevisionName,
      orgRevisionIsCurrent: revision.orgRevisionIsCurrent,
      orgRevisionCreatedAt: revision.createdAt,
      orgRevisionIsDraft: revision.orgRevisionIsDraft,
    }));

    return new HttpSuccess(mapOrgRevisions);
  }

  /**
   * API โครงสร้างปัจจุบันที่ใช้อยู่
   *
   * @summary ORG_021 - โครงสร้างปัจจุบันที่ใช้อยู่ #22
   *
   */
  @Get("active")
  async GetActive() {
    const orgRevisionActive = await this.orgRevisionRepository.findOne({
      where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
    });
    const orgRevisionDraf = await this.orgRevisionRepository.findOne({
      where: { orgRevisionIsCurrent: false, orgRevisionIsDraft: true },
    });
    const mapData = {
      activeId: orgRevisionActive == null ? null : orgRevisionActive.id,
      activeName: orgRevisionActive == null ? null : orgRevisionActive.orgRevisionName,
      draftId: orgRevisionDraf == null ? null : orgRevisionDraf.id,
      draftName: orgRevisionDraf == null ? null : orgRevisionDraf.orgRevisionName,
      orgPublishDate: orgRevisionDraf == null ? null : orgRevisionDraf.orgPublishDate,
      isPublic: orgRevisionDraf == null || orgRevisionDraf.orgRevisionName == null ? false : true,
    };
    return new HttpSuccess(mapData);
  }

  /**
   * API สร้างแบบร่างโครงสร้าง
   *
   * @summary ORG_022 - สร้างโครงสร้างใหม่ #23
   *
   */
  @Post("draft")
  async CreateOrgRevision(
    @Body() requestBody: CreateOrgRevision,
    @Request() request: RequestWithUser,
  ) {
    try {
      // CheckQueueInProgress
      const [isBusyDraft, isBusyPublish] = await Promise.all([
        checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`),
        checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`),
      ]);
      // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft);
      // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish);
      if (isBusyDraft || isBusyPublish) {
        // console.log("🚫 พบว่ามีงานอยู่ในคิว — error")
        throw new HttpError(
          HttpStatusCode.CONFLICT,
          "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน",
        );
      }
      //new main revision
      const before = null;
      const revision = Object.assign(new OrgRevision(), requestBody) as OrgRevision;
      revision.orgRevisionIsDraft = true;
      revision.orgRevisionIsCurrent = false;
      revision.createdUserId = request.user.sub;
      revision.createdFullName = request.user.name;
      revision.lastUpdateUserId = request.user.sub;
      revision.lastUpdateFullName = request.user.name;
      revision.createdAt = new Date();
      revision.lastUpdatedAt = new Date();
      await this.orgRevisionRepository.save(revision, { data: request });

      setLogDataDiff(request, { before, after: revision });
      const msg = {
        data: {
          requestBody: requestBody,
          request: request.user,
          revision: revision,
        },
        user: request.user,
      };
      await sendToQueueOrgDraft(msg);
      return new HttpSuccess("Draft is being created... Processing in the background.");
    } catch (error: any) {
      console.error("Error creating draft organization:", error);
      throw error;
    }
  }

  /**
   * API สร้างแบบร่างโครงสร้าง
   *
   * @summary ORG_022 - สร้างโครงสร้างใหม่ #23
   *
   */
  @Post("finddna")
  async FindDnaOrg(
    @Body()
    requestBody: {
      root: string[] | null;
      child1: string[] | null;
      child2: string[] | null;
      child3: string[] | null;
      child4: string[] | null;
      privilege: string;
    },
    @Request() request: RequestWithUser,
  ) {
    const _data = [];
    if (requestBody.root != null && requestBody.root.length > 0) {
      for (const x of requestBody.root) {
        const data = await this.orgRootRepository.findOne({
          where: { id: x },
        });
        if (data) {
          _data.push(data.ancestorDNA);
        }
      }
      requestBody.root = _data;
    }
    const _data1 = [];
    if (requestBody.child1 != null && requestBody.child1.length > 0) {
      for (const x of requestBody.child1) {
        const data = await this.child1Repository.findOne({
          where: { id: x },
        });
        if (data) {
          _data1.push(data.ancestorDNA);
        }
      }
      requestBody.child1 = _data1;
    }
    const _data2 = [];
    if (requestBody.child2 != null && requestBody.child2.length > 0) {
      for (const x of requestBody.child2) {
        const data = await this.child2Repository.findOne({
          where: { id: x },
        });
        if (data) {
          _data2.push(data.ancestorDNA);
        }
      }
      requestBody.child2 = _data2;
    }
    const _data3 = [];
    if (requestBody.child3 != null && requestBody.child3.length > 0) {
      for (const x of requestBody.child3) {
        const data = await this.child3Repository.findOne({
          where: { id: x },
        });
        if (data) {
          _data3.push(data.ancestorDNA);
        }
      }
      requestBody.child3 = _data3;
    }
    const _data4 = [];
    if (requestBody.child4 != null && requestBody.child4.length > 0) {
      for (const x of requestBody.child4) {
        const data = await this.child4Repository.findOne({
          where: { id: x },
        });
        if (data) {
          _data4.push(data.ancestorDNA);
        }
      }
      requestBody.child4 = _data4;
    }
    return new HttpSuccess(requestBody);
  }

  /**
   * API หา ORG DNA ID
   *
   * @summary หา ORG DNA ID
   *
   */
  @Get("finddna-by-keycloak/{keycloakId}")
  async FindDnaOrgByKeycloakId(@Path() keycloakId: string) {
    let reply: any;

    const profileByKeycloak: any = await this.profileRepo.findOne({
      where: { keycloak: keycloakId },
      select: ["id", "keycloak"],
    });

    const orgRevision = await this.orgRevisionRepository.findOne({
      select: ["id"],
      where: {
        orgRevisionIsDraft: false,
        orgRevisionIsCurrent: true,
      },
    });

    const posMaster = await AppDataSource.getRepository(PosMaster)
      .createQueryBuilder("pos")
      .leftJoin("pos.orgRoot", "orgRoot")
      .leftJoin("pos.orgChild1", "orgChild1")
      .leftJoin("pos.orgChild2", "orgChild2")
      .leftJoin("pos.orgChild3", "orgChild3")
      .leftJoin("pos.orgChild4", "orgChild4")
      .where("pos.current_holderId = :holderId", {
        holderId: profileByKeycloak.id,
      })
      .andWhere("pos.orgRevisionId = :revId", {
        revId: orgRevision?.id,
      })
      .select([
        "pos.id",
        "orgRoot.ancestorDNA as rootDnaId",
        "orgChild1.ancestorDNA as child1DnaId",
        "orgChild2.ancestorDNA as child2DnaId",
        "orgChild3.ancestorDNA as child3DnaId",
        "orgChild4.ancestorDNA as child4DnaId",
      ])
      .getRawOne();

    if (!posMaster) {
      reply = {
        rootDnaId: null,
        child1DnaId: null,
        child2DnaId: null,
        child3DnaId: null,
        child4DnaId: null,
      };
    } else {
      reply = {
        rootDnaId: posMaster.rootDnaId,
        child1DnaId: posMaster.child1DnaId,
        child2DnaId: posMaster.child2DnaId,
        child3DnaId: posMaster.child3DnaId,
        child4DnaId: posMaster.child4DnaId,
      };
    }
    return new HttpSuccess(reply);
  }

  /**
   * API เช็คสถานะโครงสร้างว่าส่งคนไปออกคำสั่งหรือไม่
   *
   * @summary API เช็คสถานะโครงสร้างว่าส่งคนไปออกคำสั่งหรือไม่
   *
   * @param {string} id Id OrgRevison
   */
  @Get("lock/{id}")
  async GetById(@Request() request: RequestWithUser, @Path() id: string) {
    //add check permission
    const orgRevision = await this.orgRevisionRepository.findOne({
      where: { id: id },
    });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้าง");
    }
    return new HttpSuccess(orgRevision.isLock);
  }

  /**
   * API รายละเอียดโครงสร้าง
   *
   * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN Menu) #25
   *
   */
  @Get("admin/{id}")
  async detailForAdmin(@Path() id: string, @Request() request: RequestWithUser) {
    // let _data: any = {
    //   root: null,
    //   child1: null,
    //   child2: null,
    //   child3: null,
    //   child4: null,
    // };

    const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }
    // let attrOwnership = null;
    if (
      orgRevision.orgRevisionIsDraft == true &&
      orgRevision.orgRevisionIsCurrent == false &&
      request.user.role.includes("SUPER_ADMIN")
      // attrOwnership == false
    ) {
      const profile = await this.profileRepo.findOne({
        where: { keycloak: request.user.sub },
        // relations: ["permissionProfiles"],
      });
      if (!profile) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ");
      }
      // _data = {
      //   root: profile.permissionProfiles.map((x) => x.orgRootId),
      //   child1: null,
      //   child2: null,
      //   child3: null,
      //   child4: null,
      // };
    }

    const orgRootData = await AppDataSource.getRepository(OrgRoot)
      .createQueryBuilder("orgRoot")
      .where("orgRoot.orgRevisionId = :id", { id })
      // .andWhere(
      //   _data.root != undefined && _data.root != null
      //     ? _data.root[0] != null
      //       ? `orgRoot.id IN (:...node)`
      //       : `orgRoot.id is null`
      //     : "1=1",
      //   {
      //     node: _data.root,
      //   },
      // )
      .select([
        "orgRoot.id",
        "orgRoot.isDeputy",
        "orgRoot.isCommission",
        "orgRoot.orgRootName",
        "orgRoot.orgRootShortName",
        "orgRoot.orgRootCode",
        "orgRoot.orgRootOrder",
        "orgRoot.orgRootPhoneEx",
        "orgRoot.orgRootPhoneIn",
        "orgRoot.orgRootFax",
        "orgRoot.orgRevisionId",
        "orgRoot.orgRootRank",
        "orgRoot.orgRootRankSub",
        "orgRoot.DEPARTMENT_CODE",
        "orgRoot.DIVISION_CODE",
        "orgRoot.SECTION_CODE",
        "orgRoot.JOB_CODE",
        "orgRoot.responsibility",
      ])
      .orderBy("orgRoot.orgRootOrder", "ASC")
      .getMany();
    const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null;
    const orgChild1Data =
      orgRootIds && orgRootIds.length > 0
        ? await AppDataSource.getRepository(OrgChild1)
            .createQueryBuilder("orgChild1")
            .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds })
            // .andWhere(
            //   _data.child1 != undefined && _data.child1 != null
            //     ? _data.child1[0] != null
            //       ? `orgChild1.id IN (:...node)`
            //       : `orgChild1.id is null`
            //     : "1=1",
            //   {
            //     node: _data.child1,
            //   },
            // )
            .select([
              "orgChild1.id",
              "orgChild1.isOfficer",
              "orgChild1.isInformation",
              "orgChild1.orgChild1Name",
              "orgChild1.orgChild1ShortName",
              "orgChild1.orgChild1Code",
              "orgChild1.orgChild1Order",
              "orgChild1.orgChild1PhoneEx",
              "orgChild1.orgChild1PhoneIn",
              "orgChild1.orgChild1Fax",
              "orgChild1.orgRootId",
              "orgChild1.orgChild1Rank",
              "orgChild1.orgChild1RankSub",
              "orgChild1.DEPARTMENT_CODE",
              "orgChild1.DIVISION_CODE",
              "orgChild1.SECTION_CODE",
              "orgChild1.JOB_CODE",
              "orgChild1.responsibility",
            ])
            .orderBy("orgChild1.orgChild1Order", "ASC")
            .getMany()
        : [];

    const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null;
    const orgChild2Data =
      orgChild1Ids && orgChild1Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild2)
            .createQueryBuilder("orgChild2")
            .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids })
            // .andWhere(
            //   _data.child2 != undefined && _data.child2 != null
            //     ? _data.child2[0] != null
            //       ? `orgChild2.id IN (:...node)`
            //       : `orgChild2.id is null`
            //     : "1=1",
            //   {
            //     node: _data.child2,
            //   },
            // )
            .select([
              "orgChild2.id",
              "orgChild2.orgChild2Name",
              "orgChild2.orgChild2ShortName",
              "orgChild2.orgChild2Code",
              "orgChild2.orgChild2Order",
              "orgChild2.orgChild2PhoneEx",
              "orgChild2.orgChild2PhoneIn",
              "orgChild2.orgChild2Fax",
              "orgChild2.orgRootId",
              "orgChild2.orgChild2Rank",
              "orgChild2.orgChild2RankSub",
              "orgChild2.DEPARTMENT_CODE",
              "orgChild2.DIVISION_CODE",
              "orgChild2.SECTION_CODE",
              "orgChild2.JOB_CODE",
              "orgChild2.orgChild1Id",
              "orgChild2.responsibility",
            ])
            .orderBy("orgChild2.orgChild2Order", "ASC")
            .getMany()
        : [];

    const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null;
    const orgChild3Data =
      orgChild2Ids && orgChild2Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild3)
            .createQueryBuilder("orgChild3")
            .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids })
            // .andWhere(
            //   _data.child3 != undefined && _data.child3 != null
            //     ? _data.child3[0] != null
            //       ? `orgChild3.id IN (:...node)`
            //       : `orgChild3.id is null`
            //     : "1=1",
            //   {
            //     node: _data.child3,
            //   },
            // )
            .select([
              "orgChild3.id",
              "orgChild3.orgChild3Name",
              "orgChild3.orgChild3ShortName",
              "orgChild3.orgChild3Code",
              "orgChild3.orgChild3Order",
              "orgChild3.orgChild3PhoneEx",
              "orgChild3.orgChild3PhoneIn",
              "orgChild3.orgChild3Fax",
              "orgChild3.orgRootId",
              "orgChild3.orgChild3Rank",
              "orgChild3.orgChild3RankSub",
              "orgChild3.DEPARTMENT_CODE",
              "orgChild3.DIVISION_CODE",
              "orgChild3.SECTION_CODE",
              "orgChild3.JOB_CODE",
              "orgChild3.orgChild2Id",
              "orgChild3.responsibility",
            ])
            .orderBy("orgChild3.orgChild3Order", "ASC")
            .getMany()
        : [];

    const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null;
    const orgChild4Data =
      orgChild3Ids && orgChild3Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild4)
            .createQueryBuilder("orgChild4")
            .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids })
            // .andWhere(
            //   _data.child4 != undefined && _data.child4 != null
            //     ? _data.child4[0] != null
            //       ? `orgChild4.id IN (:...node)`
            //       : `orgChild4.id is null`
            //     : "1=1",
            //   {
            //     node: _data.child4,
            //   },
            // )
            .select([
              "orgChild4.id",
              "orgChild4.orgChild4Name",
              "orgChild4.orgChild4ShortName",
              "orgChild4.orgChild4Code",
              "orgChild4.orgChild4Order",
              "orgChild4.orgChild4PhoneEx",
              "orgChild4.orgChild4PhoneIn",
              "orgChild4.orgChild4Fax",
              "orgChild4.orgRootId",
              "orgChild4.orgChild4Rank",
              "orgChild4.orgChild4RankSub",
              "orgChild4.DEPARTMENT_CODE",
              "orgChild4.DIVISION_CODE",
              "orgChild4.SECTION_CODE",
              "orgChild4.JOB_CODE",
              "orgChild4.orgChild3Id",
              "orgChild4.responsibility",
            ])
            .orderBy("orgChild4.orgChild4Order", "ASC")
            .getMany()
        : [];

    const formattedData = await Promise.all(
      orgRootData.map(async (orgRoot) => {
        return {
          orgTreeId: orgRoot.id,
          orgLevel: 0,
          orgName: orgRoot.orgRootName,
          orgTreeName: orgRoot.orgRootName,
          orgTreeShortName: orgRoot.orgRootShortName,
          orgTreeCode: orgRoot.orgRootCode,
          orgCode: orgRoot.orgRootCode + "00",
          orgTreeRank: orgRoot.orgRootRank,
          orgTreeRankSub: orgRoot.orgRootRankSub,
          DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE,
          DIVISION_CODE: orgRoot.DIVISION_CODE,
          SECTION_CODE: orgRoot.SECTION_CODE,
          JOB_CODE: orgRoot.JOB_CODE,
          orgTreeOrder: orgRoot.orgRootOrder,
          orgTreePhoneEx: orgRoot.orgRootPhoneEx,
          orgTreePhoneIn: orgRoot.orgRootPhoneIn,
          orgTreeFax: orgRoot.orgRootFax,
          orgRevisionId: orgRoot.orgRevisionId,
          orgRootName: orgRoot.orgRootName,
          responsibility: orgRoot.responsibility,
          isOfficer: false,
          isDeputy: orgRoot.isDeputy,
          isCommission: orgRoot.isCommission,
          labelName:
            orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName,
          totalPosition: await this.posMasterRepository.count({
            where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id },
          }),
          totalPositionCurrentUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              current_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalPositionCurrentVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              current_holderId: IsNull() || "",
            },
          }),
          totalPositionNextUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              next_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalPositionNextVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              next_holderId: IsNull() || "",
            },
          }),
          totalRootPosition: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
            },
          }),
          totalRootPositionCurrentUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              current_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalRootPositionCurrentVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              current_holderId: IsNull() || "",
            },
          }),
          totalRootPositionNextUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              next_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalRootPositionNextVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              next_holderId: IsNull() || "",
            },
          }),

          children: await Promise.all(
            orgChild1Data
              .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id)
              .map(async (orgChild1) => ({
                orgTreeId: orgChild1.id,
                orgRootId: orgRoot.id,
                orgLevel: 1,
                orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                orgTreeName: orgChild1.orgChild1Name,
                orgTreeShortName: orgChild1.orgChild1ShortName,
                orgTreeCode: orgChild1.orgChild1Code,
                orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code,
                orgTreeRank: orgChild1.orgChild1Rank,
                orgTreeRankSub: orgChild1.orgChild1RankSub,
                DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE,
                DIVISION_CODE: orgChild1.DIVISION_CODE,
                SECTION_CODE: orgChild1.SECTION_CODE,
                JOB_CODE: orgChild1.JOB_CODE,
                orgTreeOrder: orgChild1.orgChild1Order,
                orgRootCode: orgRoot.orgRootCode,
                orgTreePhoneEx: orgChild1.orgChild1PhoneEx,
                orgTreePhoneIn: orgChild1.orgChild1PhoneIn,
                orgTreeFax: orgChild1.orgChild1Fax,
                orgRevisionId: orgRoot.orgRevisionId,
                orgRootName: orgRoot.orgRootName,
                responsibility: orgChild1.responsibility,
                isOfficer: orgChild1.isOfficer,
                isInformation: orgChild1.isInformation,
                labelName:
                  orgChild1.orgChild1Name +
                  " " +
                  orgRoot.orgRootCode +
                  orgChild1.orgChild1Code +
                  " " +
                  orgChild1.orgChild1ShortName,
                totalPosition: await this.posMasterRepository.count({
                  where: { orgRevisionId: orgRoot.orgRevisionId, orgChild1Id: orgChild1.id },
                }),
                totalPositionCurrentUse: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgChild1Id: orgChild1.id,
                    current_holderId: Not(IsNull()) || Not(""),
                  },
                }),
                totalPositionCurrentVacant: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgChild1Id: orgChild1.id,
                    current_holderId: IsNull() || "",
                  },
                }),
                totalPositionNextUse: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgChild1Id: orgChild1.id,
                    next_holderId: Not(IsNull()) || Not(""),
                  },
                }),
                totalPositionNextVacant: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgChild1Id: orgChild1.id,
                    next_holderId: IsNull() || "",
                  },
                }),
                totalRootPosition: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgRootId: orgRoot.id,
                    orgChild1Id: orgChild1.id,
                    orgChild2Id: IsNull() || "",
                    orgChild3Id: IsNull() || "",
                    orgChild4Id: IsNull() || "",
                  },
                }),
                totalRootPositionCurrentUse: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgRootId: orgRoot.id,
                    orgChild1Id: orgChild1.id,
                    orgChild2Id: IsNull() || "",
                    orgChild3Id: IsNull() || "",
                    orgChild4Id: IsNull() || "",
                    current_holderId: Not(IsNull()) || Not(""),
                  },
                }),
                totalRootPositionCurrentVacant: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgRootId: orgRoot.id,
                    orgChild1Id: orgChild1.id,
                    orgChild2Id: IsNull() || "",
                    orgChild3Id: IsNull() || "",
                    orgChild4Id: IsNull() || "",
                    current_holderId: IsNull() || "",
                  },
                }),
                totalRootPositionNextUse: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgRootId: orgRoot.id,
                    orgChild1Id: orgChild1.id,
                    orgChild2Id: IsNull() || "",
                    orgChild3Id: IsNull() || "",
                    orgChild4Id: IsNull() || "",
                    next_holderId: Not(IsNull()) || Not(""),
                  },
                }),
                totalRootPositionNextVacant: await this.posMasterRepository.count({
                  where: {
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgRootId: orgRoot.id,
                    orgChild1Id: orgChild1.id,
                    orgChild2Id: IsNull() || "",
                    orgChild3Id: IsNull() || "",
                    orgChild4Id: IsNull() || "",
                    next_holderId: IsNull() || "",
                  },
                }),

                children: await Promise.all(
                  orgChild2Data
                    .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id)
                    .map(async (orgChild2) => ({
                      orgTreeId: orgChild2.id,
                      orgRootId: orgChild1.id,
                      orgLevel: 2,
                      orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                      orgTreeName: orgChild2.orgChild2Name,
                      orgTreeShortName: orgChild2.orgChild2ShortName,
                      orgTreeCode: orgChild2.orgChild2Code,
                      orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code,
                      orgTreeRank: orgChild2.orgChild2Rank,
                      orgTreeRankSub: orgChild2.orgChild2RankSub,
                      DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE,
                      DIVISION_CODE: orgChild2.DIVISION_CODE,
                      SECTION_CODE: orgChild2.SECTION_CODE,
                      JOB_CODE: orgChild2.JOB_CODE,
                      orgTreeOrder: orgChild2.orgChild2Order,
                      orgRootCode: orgRoot.orgRootCode,
                      orgTreePhoneEx: orgChild2.orgChild2PhoneEx,
                      orgTreePhoneIn: orgChild2.orgChild2PhoneIn,
                      orgTreeFax: orgChild2.orgChild2Fax,
                      orgRevisionId: orgRoot.orgRevisionId,
                      orgRootName: orgRoot.orgRootName,
                      responsibility: orgChild2.responsibility,
                      isOfficer: orgChild1.isOfficer,
                      labelName:
                        orgChild2.orgChild2Name +
                        " " +
                        orgRoot.orgRootCode +
                        orgChild2.orgChild2Code +
                        " " +
                        orgChild2.orgChild2ShortName,
                      totalPosition: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgChild2Id: orgChild2.id,
                        },
                      }),
                      totalPositionCurrentUse: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgChild2Id: orgChild2.id,
                          current_holderId: Not(IsNull()) || Not(""),
                        },
                      }),
                      totalPositionCurrentVacant: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgChild2Id: orgChild2.id,
                          current_holderId: IsNull() || "",
                        },
                      }),
                      totalPositionNextUse: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgChild2Id: orgChild2.id,
                          next_holderId: Not(IsNull()) || Not(""),
                        },
                      }),
                      totalPositionNextVacant: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgChild2Id: orgChild2.id,
                          next_holderId: IsNull() || "",
                        },
                      }),
                      totalRootPosition: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgRootId: orgRoot.id,
                          orgChild1Id: orgChild1.id,
                          orgChild2Id: orgChild2.id,
                          orgChild3Id: IsNull() || "",
                          orgChild4Id: IsNull() || "",
                        },
                      }),
                      totalRootPositionCurrentUse: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgRootId: orgRoot.id,
                          orgChild1Id: orgChild1.id,
                          orgChild2Id: orgChild2.id,
                          orgChild3Id: IsNull() || "",
                          orgChild4Id: IsNull() || "",
                          current_holderId: Not(IsNull()) || Not(""),
                        },
                      }),
                      totalRootPositionCurrentVacant: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgRootId: orgRoot.id,
                          orgChild1Id: orgChild1.id,
                          orgChild2Id: orgChild2.id,
                          orgChild3Id: IsNull() || "",
                          orgChild4Id: IsNull() || "",
                          current_holderId: IsNull() || "",
                        },
                      }),
                      totalRootPositionNextUse: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgRootId: orgRoot.id,
                          orgChild1Id: orgChild1.id,
                          orgChild2Id: orgChild2.id,
                          orgChild3Id: IsNull() || "",
                          orgChild4Id: IsNull() || "",
                          next_holderId: Not(IsNull()) || Not(""),
                        },
                      }),
                      totalRootPositionNextVacant: await this.posMasterRepository.count({
                        where: {
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgRootId: orgRoot.id,
                          orgChild1Id: orgChild1.id,
                          orgChild2Id: orgChild2.id,
                          orgChild3Id: IsNull() || "",
                          orgChild4Id: IsNull() || "",
                          next_holderId: IsNull() || "",
                        },
                      }),

                      children: await Promise.all(
                        orgChild3Data
                          .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id)
                          .map(async (orgChild3) => ({
                            orgTreeId: orgChild3.id,
                            orgRootId: orgChild2.id,
                            orgLevel: 3,
                            orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                            orgTreeName: orgChild3.orgChild3Name,
                            orgTreeShortName: orgChild3.orgChild3ShortName,
                            orgTreeCode: orgChild3.orgChild3Code,
                            orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code,
                            orgTreeRank: orgChild3.orgChild3Rank,
                            orgTreeRankSub: orgChild3.orgChild3RankSub,
                            DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE,
                            DIVISION_CODE: orgChild3.DIVISION_CODE,
                            SECTION_CODE: orgChild3.SECTION_CODE,
                            JOB_CODE: orgChild3.JOB_CODE,
                            orgTreeOrder: orgChild3.orgChild3Order,
                            orgRootCode: orgRoot.orgRootCode,
                            orgTreePhoneEx: orgChild3.orgChild3PhoneEx,
                            orgTreePhoneIn: orgChild3.orgChild3PhoneIn,
                            orgTreeFax: orgChild3.orgChild3Fax,
                            orgRevisionId: orgRoot.orgRevisionId,
                            orgRootName: orgRoot.orgRootName,
                            responsibility: orgChild3.responsibility,
                            isOfficer: orgChild1.isOfficer,
                            labelName:
                              orgChild3.orgChild3Name +
                              " " +
                              orgRoot.orgRootCode +
                              orgChild3.orgChild3Code +
                              " " +
                              orgChild3.orgChild3ShortName,
                            totalPosition: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgChild3Id: orgChild3.id,
                              },
                            }),
                            totalPositionCurrentUse: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgChild3Id: orgChild3.id,
                                current_holderId: Not(IsNull()) || Not(""),
                              },
                            }),
                            totalPositionCurrentVacant: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgChild3Id: orgChild3.id,
                                current_holderId: IsNull() || "",
                              },
                            }),
                            totalPositionNextUse: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgChild3Id: orgChild3.id,
                                next_holderId: Not(IsNull()) || Not(""),
                              },
                            }),
                            totalPositionNextVacant: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgChild3Id: orgChild3.id,
                                next_holderId: IsNull() || "",
                              },
                            }),
                            totalRootPosition: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgRootId: orgRoot.id,
                                orgChild1Id: orgChild1.id,
                                orgChild2Id: orgChild2.id,
                                orgChild3Id: orgChild3.id,
                                orgChild4Id: IsNull() || "",
                              },
                            }),
                            totalRootPositionCurrentUse: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgRootId: orgRoot.id,
                                orgChild1Id: orgChild1.id,
                                orgChild2Id: orgChild2.id,
                                orgChild3Id: orgChild3.id,
                                orgChild4Id: IsNull() || "",
                                current_holderId: Not(IsNull()) || Not(""),
                              },
                            }),
                            totalRootPositionCurrentVacant: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgRootId: orgRoot.id,
                                orgChild1Id: orgChild1.id,
                                orgChild2Id: orgChild2.id,
                                orgChild3Id: orgChild3.id,
                                orgChild4Id: IsNull() || "",
                                current_holderId: IsNull() || "",
                              },
                            }),
                            totalRootPositionNextUse: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgRootId: orgRoot.id,
                                orgChild1Id: orgChild1.id,
                                orgChild2Id: orgChild2.id,
                                orgChild3Id: orgChild3.id,
                                orgChild4Id: IsNull() || "",
                                next_holderId: Not(IsNull()) || Not(""),
                              },
                            }),
                            totalRootPositionNextVacant: await this.posMasterRepository.count({
                              where: {
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgRootId: orgRoot.id,
                                orgChild1Id: orgChild1.id,
                                orgChild2Id: orgChild2.id,
                                orgChild3Id: orgChild3.id,
                                orgChild4Id: IsNull() || "",
                                next_holderId: IsNull() || "",
                              },
                            }),

                            children: await Promise.all(
                              orgChild4Data
                                .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id)
                                .map(async (orgChild4) => ({
                                  orgTreeId: orgChild4.id,
                                  orgRootId: orgChild3.id,
                                  orgLevel: 4,
                                  orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                                  orgTreeName: orgChild4.orgChild4Name,
                                  orgTreeShortName: orgChild4.orgChild4ShortName,
                                  orgTreeCode: orgChild4.orgChild4Code,
                                  orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code,
                                  orgTreeRank: orgChild4.orgChild4Rank,
                                  orgTreeRankSub: orgChild4.orgChild4RankSub,
                                  DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE,
                                  DIVISION_CODE: orgChild4.DIVISION_CODE,
                                  SECTION_CODE: orgChild4.SECTION_CODE,
                                  JOB_CODE: orgChild4.JOB_CODE,
                                  orgTreeOrder: orgChild4.orgChild4Order,
                                  orgRootCode: orgRoot.orgRootCode,
                                  orgTreePhoneEx: orgChild4.orgChild4PhoneEx,
                                  orgTreePhoneIn: orgChild4.orgChild4PhoneIn,
                                  orgTreeFax: orgChild4.orgChild4Fax,
                                  orgRevisionId: orgRoot.orgRevisionId,
                                  orgRootName: orgRoot.orgRootName,
                                  responsibility: orgChild4.responsibility,
                                  isOfficer: orgChild1.isOfficer,
                                  labelName:
                                    orgChild4.orgChild4Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild4.orgChild4Code +
                                    " " +
                                    orgChild4.orgChild4ShortName,
                                  totalPosition: await this.posMasterRepository.count({
                                    where: {
                                      orgRevisionId: orgRoot.orgRevisionId,
                                      orgChild4Id: orgChild4.id,
                                    },
                                  }),
                                  totalPositionCurrentUse: await this.posMasterRepository.count({
                                    where: {
                                      orgRevisionId: orgRoot.orgRevisionId,
                                      orgChild4Id: orgChild4.id,
                                      current_holderId: Not(IsNull()) || Not(""),
                                    },
                                  }),
                                  totalPositionCurrentVacant: await this.posMasterRepository.count({
                                    where: {
                                      orgRevisionId: orgRoot.orgRevisionId,
                                      orgChild4Id: orgChild4.id,
                                      current_holderId: IsNull() || "",
                                    },
                                  }),
                                  totalPositionNextUse: await this.posMasterRepository.count({
                                    where: {
                                      orgRevisionId: orgRoot.orgRevisionId,
                                      orgChild4Id: orgChild4.id,
                                      next_holderId: Not(IsNull()) || Not(""),
                                    },
                                  }),
                                  totalPositionNextVacant: await this.posMasterRepository.count({
                                    where: {
                                      orgRevisionId: orgRoot.orgRevisionId,
                                      orgChild4Id: orgChild4.id,
                                      next_holderId: IsNull() || "",
                                    },
                                  }),
                                  totalRootPosition: await this.posMasterRepository.count({
                                    where: {
                                      orgRevisionId: orgRoot.orgRevisionId,
                                      orgRootId: orgRoot.id,
                                      orgChild1Id: orgChild1.id,
                                      orgChild2Id: orgChild2.id,
                                      orgChild3Id: orgChild3.id,
                                      orgChild4Id: orgChild4.id,
                                    },
                                  }),
                                  totalRootPositionCurrentUse: await this.posMasterRepository.count(
                                    {
                                      where: {
                                        orgRevisionId: orgRoot.orgRevisionId,
                                        orgRootId: orgRoot.id,
                                        orgChild1Id: orgChild1.id,
                                        orgChild2Id: orgChild2.id,
                                        orgChild3Id: orgChild3.id,
                                        orgChild4Id: orgChild4.id,
                                        current_holderId: Not(IsNull()) || Not(""),
                                      },
                                    },
                                  ),
                                  totalRootPositionCurrentVacant:
                                    await this.posMasterRepository.count({
                                      where: {
                                        orgRevisionId: orgRoot.orgRevisionId,
                                        orgRootId: orgRoot.id,
                                        orgChild1Id: orgChild1.id,
                                        orgChild2Id: orgChild2.id,
                                        orgChild3Id: orgChild3.id,
                                        orgChild4Id: orgChild4.id,
                                        current_holderId: IsNull() || "",
                                      },
                                    }),
                                  totalRootPositionNextUse: await this.posMasterRepository.count({
                                    where: {
                                      orgRevisionId: orgRoot.orgRevisionId,
                                      orgRootId: orgRoot.id,
                                      orgChild1Id: orgChild1.id,
                                      orgChild2Id: orgChild2.id,
                                      orgChild3Id: orgChild3.id,
                                      orgChild4Id: orgChild4.id,
                                      next_holderId: Not(IsNull()) || Not(""),
                                    },
                                  }),
                                  totalRootPositionNextVacant: await this.posMasterRepository.count(
                                    {
                                      where: {
                                        orgRevisionId: orgRoot.orgRevisionId,
                                        orgRootId: orgRoot.id,
                                        orgChild1Id: orgChild1.id,
                                        orgChild2Id: orgChild2.id,
                                        orgChild3Id: orgChild3.id,
                                        orgChild4Id: orgChild4.id,
                                        next_holderId: IsNull() || "",
                                      },
                                    },
                                  ),
                                })),
                            ),
                          })),
                      ),
                    })),
                ),
              })),
          ),
        };
      }),
    );

    return new HttpSuccess(formattedData);
  }

  /**
   * API รายละเอียดโครงสร้าง
   *
   * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25
   *
   */
  @Get("super-admin/{id}")
  async detailSuperAdmin(@Path() id: string, @Request() request: RequestWithUser) {
    const orgRevision = await this.orgRevisionRepository.findOne({
      where: { id: id },
    });
    if (!orgRevision) return new HttpSuccess([]);

    let rootId: any = null;
    if (!request.user.role.includes("SUPER_ADMIN")) {
      const profile = await this.profileRepo.findOne({
        where: {
          keycloak: request.user.sub,
        },
        select: ["id"],
      });
      if (profile == null) return new HttpSuccess([]);

      const posMaster = await this.posMasterRepository.findOne({
        where:
          orgRevision.orgRevisionIsCurrent && !orgRevision.orgRevisionIsDraft
            ? {
                orgRevisionId: id,
                current_holderId: profile.id,
              }
            : {
                orgRevisionId: id,
                next_holderId: profile.id,
              },
      });
      if (!posMaster) return new HttpSuccess([]);

      rootId = posMaster.orgRootId;
    }

    // OPTIMIZED: Check cache first
    const cachedResponse = await orgStructureCache.get(id, rootId);
    if (cachedResponse) {
      return new HttpSuccess(cachedResponse);
    }

    // OPTIMIZED: Get all position counts in ONE query (closed)
    // const { orgRootMap, orgChild1Map, orgChild2Map, orgChild3Map, orgChild4Map, rootPosMap } =
    //   await getPositionCounts(id);

    // OPTIMIZED: Fetch orgRoot first, then fetch all child levels in parallel
    const orgRootData = await AppDataSource.getRepository(OrgRoot)
      .createQueryBuilder("orgRoot")
      .where("orgRoot.orgRevisionId = :id", { id })
      .andWhere(rootId != null ? `orgRoot.id = :rootId` : "1=1", {
        rootId: rootId,
      })
      .orderBy("orgRoot.orgRootOrder", "ASC")
      .getMany();

    const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id);

    // OPTIMIZED: Fetch all child levels in parallel using orgRevisionId
    // This is faster than sequential queries that depend on parent IDs
    const [orgChild1AllData, orgChild2AllData, orgChild3AllData, orgChild4AllData] =
      await Promise.all([
        AppDataSource.getRepository(OrgChild1)
          .createQueryBuilder("orgChild1")
          .where("orgChild1.orgRevisionId = :id", { id })
          .orderBy("orgChild1.orgChild1Order", "ASC")
          .getMany(),

        AppDataSource.getRepository(OrgChild2)
          .createQueryBuilder("orgChild2")
          .where("orgChild2.orgRevisionId = :id", { id })
          .orderBy("orgChild2.orgChild2Order", "ASC")
          .getMany(),

        AppDataSource.getRepository(OrgChild3)
          .createQueryBuilder("orgChild3")
          .where("orgChild3.orgRevisionId = :id", { id })
          .orderBy("orgChild3.orgChild3Order", "ASC")
          .getMany(),

        AppDataSource.getRepository(OrgChild4)
          .createQueryBuilder("orgChild4")
          .where("orgChild4.orgRevisionId = :id", { id })
          .orderBy("orgChild4.orgChild4Order", "ASC")
          .getMany(),
      ]);

    // Filter child1 data by orgRootIds (maintains backward compatibility)
    const orgChild1Data =
      orgRootIds && orgRootIds.length > 0
        ? orgChild1AllData.filter((orgChild1) => orgRootIds.includes(orgChild1.orgRootId))
        : [];

    // Build maps for efficient filtering of deeper levels
    const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id);
    const orgChild2Data =
      orgChild1Ids && orgChild1Ids.length > 0
        ? orgChild2AllData.filter((orgChild2) => orgChild1Ids.includes(orgChild2.orgChild1Id))
        : [];

    const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id);
    const orgChild3Data =
      orgChild2Ids && orgChild2Ids.length > 0
        ? orgChild3AllData.filter((orgChild3) => orgChild2Ids.includes(orgChild3.orgChild2Id))
        : [];

    const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id);
    const orgChild4Data =
      orgChild3Ids && orgChild3Ids.length > 0
        ? orgChild4AllData.filter((orgChild4) => orgChild3Ids.includes(orgChild4.orgChild3Id))
        : [];

    // OPTIMIZED: Build formatted data using pre-calculated counts (no nested queries!)
    const formattedData = orgRootData.map((orgRoot) => {
      // const rootCounts = getCounts(orgRootMap, orgRoot.id);
      // const rootPosCounts = getRootCounts(rootPosMap, orgRoot.id);

      return {
        orgTreeId: orgRoot.id,
        orgLevel: 0,
        orgName: orgRoot.orgRootName,
        orgTreeName: orgRoot.orgRootName,
        orgTreeShortName: orgRoot.orgRootShortName,
        orgTreeCode: orgRoot.orgRootCode,
        orgCode: orgRoot.orgRootCode + "00",
        orgTreeRank: orgRoot.orgRootRank,
        orgTreeRankSub: orgRoot.orgRootRankSub,
        orgRootDnaId: orgRoot.ancestorDNA,
        DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE,
        DIVISION_CODE: orgRoot.DIVISION_CODE,
        SECTION_CODE: orgRoot.SECTION_CODE,
        JOB_CODE: orgRoot.JOB_CODE,
        orgTreeOrder: orgRoot.orgRootOrder,
        orgTreePhoneEx: orgRoot.orgRootPhoneEx,
        orgTreePhoneIn: orgRoot.orgRootPhoneIn,
        orgTreeFax: orgRoot.orgRootFax,
        orgRevisionId: orgRoot.orgRevisionId,
        orgRootName: orgRoot.orgRootName,
        isDeputy: orgRoot.isDeputy,
        isCommission: orgRoot.isCommission,
        responsibility: orgRoot.responsibility,
        labelName:
          orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName,
        // totalPosition: rootCounts.totalPosition,
        // totalPositionCurrentUse: rootCounts.totalPositionCurrentUse,
        // totalPositionCurrentVacant: rootCounts.totalPositionCurrentVacant,
        // totalPositionNextUse: rootCounts.totalPositionNextUse,
        // totalPositionNextVacant: rootCounts.totalPositionNextVacant,
        // totalRootPosition: rootPosCounts.totalRootPosition,
        // totalRootPositionCurrentUse: rootPosCounts.totalRootPositionCurrentUse,
        // totalRootPositionCurrentVacant: rootPosCounts.totalRootPositionCurrentVacant,
        // totalRootPositionNextUse: rootPosCounts.totalRootPositionNextUse,
        // totalRootPositionNextVacant: rootPosCounts.totalRootPositionNextVacant,
        children: orgChild1Data
          .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id)
          .map((orgChild1) => {
            // const child1Counts = getCounts(orgChild1Map, orgChild1.id);
            // const child1PosKey = `${orgRoot.id}-${orgChild1.id}`;
            // const child1PosCounts = getRootCounts(rootPosMap, child1PosKey);

            return {
              orgTreeId: orgChild1.id,
              orgRootId: orgRoot.id,
              orgLevel: 1,
              orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
              orgTreeName: orgChild1.orgChild1Name,
              orgTreeShortName: orgChild1.orgChild1ShortName,
              orgTreeCode: orgChild1.orgChild1Code,
              orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code,
              orgTreeRank: orgChild1.orgChild1Rank,
              orgTreeRankSub: orgChild1.orgChild1RankSub,
              orgRootDnaId: orgRoot.ancestorDNA,
              orgChild1DnaId: orgChild1.ancestorDNA,
              DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE,
              DIVISION_CODE: orgChild1.DIVISION_CODE,
              SECTION_CODE: orgChild1.SECTION_CODE,
              JOB_CODE: orgChild1.JOB_CODE,
              orgTreeOrder: orgChild1.orgChild1Order,
              orgRootCode: orgRoot.orgRootCode,
              orgTreePhoneEx: orgChild1.orgChild1PhoneEx,
              orgTreePhoneIn: orgChild1.orgChild1PhoneIn,
              orgTreeFax: orgChild1.orgChild1Fax,
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootName: orgRoot.orgRootName,
              responsibility: orgChild1.responsibility,
              isOfficer: orgChild1.isOfficer,
              isInformation: orgChild1.isInformation,
              labelName:
                orgChild1.orgChild1Name +
                " " +
                orgRoot.orgRootCode +
                orgChild1.orgChild1Code +
                " " +
                orgChild1.orgChild1ShortName,
              // totalPosition: child1Counts.totalPosition,
              // totalPositionCurrentUse: child1Counts.totalPositionCurrentUse,
              // totalPositionCurrentVacant: child1Counts.totalPositionCurrentVacant,
              // totalPositionNextUse: child1Counts.totalPositionNextUse,
              // totalPositionNextVacant: child1Counts.totalPositionNextVacant,
              // totalRootPosition: child1PosCounts.totalRootPosition,
              // totalRootPositionCurrentUse: child1PosCounts.totalRootPositionCurrentUse,
              // totalRootPositionCurrentVacant: child1PosCounts.totalRootPositionCurrentVacant,
              // totalRootPositionNextUse: child1PosCounts.totalRootPositionNextUse,
              // totalRootPositionNextVacant: child1PosCounts.totalRootPositionNextVacant,
              children: orgChild2Data
                .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id)
                .map((orgChild2) => {
                  // const child2Counts = getCounts(orgChild2Map, orgChild2.id);
                  // const child2PosKey = `${orgRoot.id}-${orgChild1.id}-${orgChild2.id}`;
                  // const child2PosCounts = getRootCounts(rootPosMap, child2PosKey);

                  return {
                    orgTreeId: orgChild2.id,
                    orgRootId: orgChild1.id,
                    orgLevel: 2,
                    orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                    orgTreeName: orgChild2.orgChild2Name,
                    orgTreeShortName: orgChild2.orgChild2ShortName,
                    orgTreeCode: orgChild2.orgChild2Code,
                    orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code,
                    orgTreeRank: orgChild2.orgChild2Rank,
                    orgTreeRankSub: orgChild2.orgChild2RankSub,
                    orgRootDnaId: orgRoot.ancestorDNA,
                    orgChild1DnaId: orgChild1.ancestorDNA,
                    orgChild2DnaId: orgChild2.ancestorDNA,
                    DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE,
                    DIVISION_CODE: orgChild2.DIVISION_CODE,
                    SECTION_CODE: orgChild2.SECTION_CODE,
                    JOB_CODE: orgChild2.JOB_CODE,
                    orgTreeOrder: orgChild2.orgChild2Order,
                    orgRootCode: orgRoot.orgRootCode,
                    orgTreePhoneEx: orgChild2.orgChild2PhoneEx,
                    orgTreePhoneIn: orgChild2.orgChild2PhoneIn,
                    orgTreeFax: orgChild2.orgChild2Fax,
                    orgRevisionId: orgRoot.orgRevisionId,
                    orgRootName: orgRoot.orgRootName,
                    responsibility: orgChild2.responsibility,
                    labelName:
                      orgChild2.orgChild2Name +
                      " " +
                      orgRoot.orgRootCode +
                      orgChild2.orgChild2Code +
                      " " +
                      orgChild2.orgChild2ShortName,
                    // totalPosition: child2Counts.totalPosition,
                    // totalPositionCurrentUse: child2Counts.totalPositionCurrentUse,
                    // totalPositionCurrentVacant: child2Counts.totalPositionCurrentVacant,
                    // totalPositionNextUse: child2Counts.totalPositionNextUse,
                    // totalPositionNextVacant: child2Counts.totalPositionNextVacant,
                    // totalRootPosition: child2PosCounts.totalRootPosition,
                    // totalRootPositionCurrentUse: child2PosCounts.totalRootPositionCurrentUse,
                    // totalRootPositionCurrentVacant: child2PosCounts.totalRootPositionCurrentVacant,
                    // totalRootPositionNextUse: child2PosCounts.totalRootPositionNextUse,
                    // totalRootPositionNextVacant: child2PosCounts.totalRootPositionNextVacant,
                    children: orgChild3Data
                      .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id)
                      .map((orgChild3) => {
                        // const child3Counts = getCounts(orgChild3Map, orgChild3.id);
                        // const child3PosKey = `${orgRoot.id}-${orgChild1.id}-${orgChild2.id}-${orgChild3.id}`;
                        // const child3PosCounts = getRootCounts(rootPosMap, child3PosKey);

                        return {
                          orgTreeId: orgChild3.id,
                          orgRootId: orgChild2.id,
                          orgLevel: 3,
                          orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                          orgTreeName: orgChild3.orgChild3Name,
                          orgTreeShortName: orgChild3.orgChild3ShortName,
                          orgTreeCode: orgChild3.orgChild3Code,
                          orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code,
                          orgTreeRank: orgChild3.orgChild3Rank,
                          orgTreeRankSub: orgChild3.orgChild3RankSub,
                          orgRootDnaId: orgRoot.ancestorDNA,
                          orgChild1DnaId: orgChild1.ancestorDNA,
                          orgChild2DnaId: orgChild2.ancestorDNA,
                          orgChild3DnaId: orgChild3.ancestorDNA,
                          DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE,
                          DIVISION_CODE: orgChild3.DIVISION_CODE,
                          SECTION_CODE: orgChild3.SECTION_CODE,
                          JOB_CODE: orgChild3.JOB_CODE,
                          orgTreeOrder: orgChild3.orgChild3Order,
                          orgRootCode: orgRoot.orgRootCode,
                          orgTreePhoneEx: orgChild3.orgChild3PhoneEx,
                          orgTreePhoneIn: orgChild3.orgChild3PhoneIn,
                          orgTreeFax: orgChild3.orgChild3Fax,
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgRootName: orgRoot.orgRootName,
                          responsibility: orgChild3.responsibility,
                          labelName:
                            orgChild3.orgChild3Name +
                            " " +
                            orgRoot.orgRootCode +
                            orgChild3.orgChild3Code +
                            " " +
                            orgChild3.orgChild3ShortName,
                          // totalPosition: child3Counts.totalPosition,
                          // totalPositionCurrentUse: child3Counts.totalPositionCurrentUse,
                          // totalPositionCurrentVacant: child3Counts.totalPositionCurrentVacant,
                          // totalPositionNextUse: child3Counts.totalPositionNextUse,
                          // totalPositionNextVacant: child3Counts.totalPositionNextVacant,
                          // totalRootPosition: child3PosCounts.totalRootPosition,
                          // totalRootPositionCurrentUse: child3PosCounts.totalRootPositionCurrentUse,
                          // totalRootPositionCurrentVacant:
                          //   child3PosCounts.totalRootPositionCurrentVacant,
                          // totalRootPositionNextUse: child3PosCounts.totalRootPositionNextUse,
                          // totalRootPositionNextVacant: child3PosCounts.totalRootPositionNextVacant,
                          children: orgChild4Data
                            .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id)
                            .map((orgChild4) => {
                              // const child4Counts = getCounts(orgChild4Map, orgChild4.id);
                              // const child4PosKey = `${orgRoot.id}-${orgChild1.id}-${orgChild2.id}-${orgChild3.id}-${orgChild4.id}`;
                              // const child4PosCounts = getRootCounts(rootPosMap, child4PosKey);

                              return {
                                orgTreeId: orgChild4.id,
                                orgRootId: orgChild3.id,
                                orgLevel: 4,
                                orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                                orgTreeName: orgChild4.orgChild4Name,
                                orgTreeShortName: orgChild4.orgChild4ShortName,
                                orgTreeCode: orgChild4.orgChild4Code,
                                orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code,
                                orgTreeRank: orgChild4.orgChild4Rank,
                                orgTreeRankSub: orgChild4.orgChild4RankSub,
                                orgRootDnaId: orgRoot.ancestorDNA,
                                orgChild1DnaId: orgChild1.ancestorDNA,
                                orgChild2DnaId: orgChild2.ancestorDNA,
                                orgChild3DnaId: orgChild3.ancestorDNA,
                                orgChild4DnaId: orgChild4.ancestorDNA,
                                DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE,
                                DIVISION_CODE: orgChild4.DIVISION_CODE,
                                SECTION_CODE: orgChild4.SECTION_CODE,
                                JOB_CODE: orgChild4.JOB_CODE,
                                orgTreeOrder: orgChild4.orgChild4Order,
                                orgRootCode: orgRoot.orgRootCode,
                                orgTreePhoneEx: orgChild4.orgChild4PhoneEx,
                                orgTreePhoneIn: orgChild4.orgChild4PhoneIn,
                                orgTreeFax: orgChild4.orgChild4Fax,
                                orgRevisionId: orgRoot.orgRevisionId,
                                orgRootName: orgRoot.orgRootName,
                                responsibility: orgChild4.responsibility,
                                labelName:
                                  orgChild4.orgChild4Name +
                                  " " +
                                  orgRoot.orgRootCode +
                                  orgChild4.orgChild4Code +
                                  " " +
                                  orgChild4.orgChild4ShortName,
                                // totalPosition: child4Counts.totalPosition,
                                // totalPositionCurrentUse: child4Counts.totalPositionCurrentUse,
                                // totalPositionCurrentVacant: child4Counts.totalPositionCurrentVacant,
                                // totalPositionNextUse: child4Counts.totalPositionNextUse,
                                // totalPositionNextVacant: child4Counts.totalPositionNextVacant,
                                // totalRootPosition: child4PosCounts.totalRootPosition,
                                // totalRootPositionCurrentUse:
                                //   child4PosCounts.totalRootPositionCurrentUse,
                                // totalRootPositionCurrentVacant:
                                //   child4PosCounts.totalRootPositionCurrentVacant,
                                // totalRootPositionNextUse: child4PosCounts.totalRootPositionNextUse,
                                // totalRootPositionNextVacant:
                                //   child4PosCounts.totalRootPositionNextVacant,
                                children: [],
                              };
                            }),
                        };
                      }),
                  };
                }),
            };
          }),
      };
    });

    // OPTIMIZED: Cache the result
    await orgStructureCache.set(id, rootId, formattedData);

    return new HttpSuccess(formattedData);
  }

  /**
   * API รายละเอียดโครงสร้าง
   *
   * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25
   *
   */
  @Get("{id}")
  async detail(@Path() id: string, @Request() request: RequestWithUser) {
    let _data: any = {
      root: null,
      child1: null,
      child2: null,
      child3: null,
      child4: null,
    };
    let _data1: any = {
      root: null,
    };

    const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } });

    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }
    let _privilege = await new permission().PermissionOrgList(request, "SYS_ORG");

    const attrOwnership = _privilege.root === null ? true : false;

    const profile = await this.profileRepo.findOne({
      where: { keycloak: request.user.sub },
      relations: ["permissionProfiles", "current_holders", "current_holders.posMasterAssigns"],
    });
    if (!profile) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ");
    }
    let profileAssign = profile.current_holders
      ?.find((x) => x.orgRevisionId === id)
      ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG");

    if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) {
      if (Array.isArray(profile.permissionProfiles) && profile.permissionProfiles.length > 0) {
        _data.root = profile.permissionProfiles.map((x) => x.orgRootId);
      } else {
        return new HttpSuccess({ remark: "", data: [] });
      }
    }
    // กำหนดการเข้าถึงข้อมูลตามสถานะและสิทธิ์
    const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent;
    if (isCurrentActive) {
      if (profileAssign && _privilege.privilege !== "OWNER" && _privilege.privilege !== "PARENT") {
        if (_privilege.privilege == "NORMAL") {
          const holder = profile.current_holders.find((x) => x.orgRevisionId === id);
          if (!holder) return;
          _data.root = [holder.orgRootId];
          _data.child1 = [holder.orgChild1Id];
          _data.child2 = [holder.orgChild2Id];
          _data.child3 = [holder.orgChild3Id];
          _data.child4 = [holder.orgChild4Id];
        } else if (_privilege.privilege == "CHILD" || _privilege.privilege == "BROTHER") {
          const holder = profile.current_holders.find((x) => x.orgRevisionId === id);
          if (!holder) return;
          _data.root = [holder.orgRootId];
          if (_privilege.root && _privilege.child1 === null) {
          } else if (_privilege.child1 && _privilege.child2 === null) {
            _data.child1 = [holder.orgChild1Id];
          } else if (_privilege.child2 && _privilege.child3 === null) {
            _data.child1 = [holder.orgChild1Id];
            _data.child2 = [holder.orgChild2Id];
          } else if (_privilege.child3 && _privilege.child4 === null) {
            _data.child1 = [holder.orgChild1Id];
            _data.child2 = [holder.orgChild2Id];
            _data.child3 = [holder.orgChild3Id];
            _data.child4 = [holder.orgChild4Id];
          }
        } else {
          _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId];
        }
      } else {
        if (!attrOwnership) _data = _privilege;
      }
    }

    const _revision = await this.orgRevisionRepository.findOne({
      where: { id: id },
    });
    if (!_revision) throw new HttpError(HttpStatusCode.NOT_FOUND, "not found.");

    const orgRootData = await AppDataSource.getRepository(OrgRoot)
      .createQueryBuilder("orgRoot")
      .where("orgRoot.orgRevisionId = :id", { id })
      .andWhere(
        _data.root !== undefined && _data.root !== null
          ? _data.root[0] !== null
            ? `orgRoot.id IN (:...node)`
            : `orgRoot.id is null`
          : "1=1",
        {
          node: _data.root,
        },
      )
      .select([
        "orgRoot.ancestorDNA",
        "orgRoot.id",
        "orgRoot.misId",
        "orgRoot.isDeputy",
        "orgRoot.isCommission",
        "orgRoot.orgRootName",
        "orgRoot.orgRootShortName",
        "orgRoot.orgRootCode",
        "orgRoot.orgRootOrder",
        "orgRoot.orgRootPhoneEx",
        "orgRoot.orgRootPhoneIn",
        "orgRoot.orgRootFax",
        "orgRoot.orgRevisionId",
        "orgRoot.orgRootRank",
        "orgRoot.orgRootRankSub",
        "orgRoot.DEPARTMENT_CODE",
        "orgRoot.DIVISION_CODE",
        "orgRoot.SECTION_CODE",
        "orgRoot.JOB_CODE",
        "orgRoot.responsibility",
      ])
      .orderBy("orgRoot.orgRootOrder", "ASC")
      .getMany();
    const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null;
    const orgChild1Data =
      orgRootIds && orgRootIds.length > 0
        ? await AppDataSource.getRepository(OrgChild1)
            .createQueryBuilder("orgChild1")
            .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds })
            .andWhere(
              _data.child1 !== undefined && _data.child1 !== null
                ? _data.child1[0] !== null
                  ? `orgChild1.id IN (:...node)`
                  : `orgChild1.id is null`
                : "1=1",
              {
                node: _data.child1,
              },
            )
            .select([
              "orgChild1.id",
              "orgChild1.misId",
              "orgChild1.isOfficer",
              "orgChild1.isInformation",
              "orgChild1.orgChild1Name",
              "orgChild1.orgChild1ShortName",
              "orgChild1.orgChild1Code",
              "orgChild1.orgChild1Order",
              "orgChild1.orgChild1PhoneEx",
              "orgChild1.orgChild1PhoneIn",
              "orgChild1.orgChild1Fax",
              "orgChild1.orgRootId",
              "orgChild1.orgChild1Rank",
              "orgChild1.orgChild1RankSub",
              "orgChild1.DEPARTMENT_CODE",
              "orgChild1.DIVISION_CODE",
              "orgChild1.SECTION_CODE",
              "orgChild1.JOB_CODE",
              "orgChild1.responsibility",
            ])
            .orderBy("orgChild1.orgChild1Order", "ASC")
            .getMany()
        : [];

    const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null;
    const orgChild2Data =
      orgChild1Ids && orgChild1Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild2)
            .createQueryBuilder("orgChild2")
            .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids })
            .andWhere(
              _data.child2 !== undefined && _data.child2 !== null
                ? _data.child2[0] !== null
                  ? `orgChild2.id IN (:...node)`
                  : `orgChild2.id is null`
                : "1=1",
              {
                node: _data.child2,
              },
            )
            .select([
              "orgChild2.id",
              "orgChild2.misId",
              "orgChild2.orgChild2Name",
              "orgChild2.orgChild2ShortName",
              "orgChild2.orgChild2Code",
              "orgChild2.orgChild2Order",
              "orgChild2.orgChild2PhoneEx",
              "orgChild2.orgChild2PhoneIn",
              "orgChild2.orgChild2Fax",
              "orgChild2.orgRootId",
              "orgChild2.orgChild2Rank",
              "orgChild2.orgChild2RankSub",
              "orgChild2.DEPARTMENT_CODE",
              "orgChild2.DIVISION_CODE",
              "orgChild2.SECTION_CODE",
              "orgChild2.JOB_CODE",
              "orgChild2.orgChild1Id",
              "orgChild2.responsibility",
            ])
            .orderBy("orgChild2.orgChild2Order", "ASC")
            .getMany()
        : [];

    const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null;
    const orgChild3Data =
      orgChild2Ids && orgChild2Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild3)
            .createQueryBuilder("orgChild3")
            .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids })
            .andWhere(
              _data.child3 !== undefined && _data.child3 !== null
                ? _data.child3[0] !== null
                  ? `orgChild3.id IN (:...node)`
                  : `orgChild3.id is null`
                : "1=1",
              {
                node: _data.child3,
              },
            )
            .select([
              "orgChild3.id",
              "orgChild3.misId",
              "orgChild3.orgChild3Name",
              "orgChild3.orgChild3ShortName",
              "orgChild3.orgChild3Code",
              "orgChild3.orgChild3Order",
              "orgChild3.orgChild3PhoneEx",
              "orgChild3.orgChild3PhoneIn",
              "orgChild3.orgChild3Fax",
              "orgChild3.orgRootId",
              "orgChild3.orgChild3Rank",
              "orgChild3.orgChild3RankSub",
              "orgChild3.DEPARTMENT_CODE",
              "orgChild3.DIVISION_CODE",
              "orgChild3.SECTION_CODE",
              "orgChild3.JOB_CODE",
              "orgChild3.orgChild2Id",
              "orgChild3.responsibility",
            ])
            .orderBy("orgChild3.orgChild3Order", "ASC")
            .getMany()
        : [];

    const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null;
    const orgChild4Data =
      orgChild3Ids && orgChild3Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild4)
            .createQueryBuilder("orgChild4")
            .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids })
            .andWhere(
              _data.child4 !== undefined && _data.child4 !== null
                ? _data.child4[0] !== null
                  ? `orgChild4.id IN (:...node)`
                  : `orgChild4.id is null`
                : "1=1",
              {
                node: _data.child4,
              },
            )
            .select([
              "orgChild4.id",
              "orgChild4.misId",
              "orgChild4.orgChild4Name",
              "orgChild4.orgChild4ShortName",
              "orgChild4.orgChild4Code",
              "orgChild4.orgChild4Order",
              "orgChild4.orgChild4PhoneEx",
              "orgChild4.orgChild4PhoneIn",
              "orgChild4.orgChild4Fax",
              "orgChild4.orgRootId",
              "orgChild4.orgChild4Rank",
              "orgChild4.orgChild4RankSub",
              "orgChild4.DEPARTMENT_CODE",
              "orgChild4.DIVISION_CODE",
              "orgChild4.SECTION_CODE",
              "orgChild4.JOB_CODE",
              "orgChild4.orgChild3Id",
              "orgChild4.responsibility",
            ])
            .orderBy("orgChild4.orgChild4Order", "ASC")
            .getMany()
        : [];

    const formattedData = await Promise.all(
      orgRootData.map(async (orgRoot) => {
        return {
          orgDnaId: orgRoot.ancestorDNA,
          orgTreeId: orgRoot.id,
          orgLevel: 0,
          misId: orgRoot.misId,
          orgName: orgRoot.orgRootName,
          orgTreeName: orgRoot.orgRootName,
          orgTreeShortName: orgRoot.orgRootShortName,
          orgTreeCode: orgRoot.orgRootCode,
          orgCode: orgRoot.orgRootCode + "00",
          orgTreeRank: orgRoot.orgRootRank,
          orgTreeRankSub: orgRoot.orgRootRankSub,
          DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE,
          DIVISION_CODE: orgRoot.DIVISION_CODE,
          SECTION_CODE: orgRoot.SECTION_CODE,
          JOB_CODE: orgRoot.JOB_CODE,
          orgTreeOrder: orgRoot.orgRootOrder,
          orgTreePhoneEx: orgRoot.orgRootPhoneEx,
          orgTreePhoneIn: orgRoot.orgRootPhoneIn,
          orgTreeFax: orgRoot.orgRootFax,
          orgRevisionId: orgRoot.orgRevisionId,
          orgRootName: orgRoot.orgRootName,
          isDeputy: orgRoot.isDeputy,
          isCommission: orgRoot.isCommission,
          responsibility: orgRoot.responsibility,
          labelName:
            orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName,

          children: await Promise.all(
            orgChild1Data
              .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id)
              .map(async (orgChild1) => ({
                orgTreeId: orgChild1.id,
                orgRootId: orgRoot.id,
                orgLevel: 1,
                misId: orgChild1.misId,
                orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                orgTreeName: orgChild1.orgChild1Name,
                orgTreeShortName: orgChild1.orgChild1ShortName,
                orgTreeCode: orgChild1.orgChild1Code,
                orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code,
                orgTreeRank: orgChild1.orgChild1Rank,
                orgTreeRankSub: orgChild1.orgChild1RankSub,
                DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE,
                DIVISION_CODE: orgChild1.DIVISION_CODE,
                SECTION_CODE: orgChild1.SECTION_CODE,
                JOB_CODE: orgChild1.JOB_CODE,
                orgTreeOrder: orgChild1.orgChild1Order,
                orgRootCode: orgRoot.orgRootCode,
                orgTreePhoneEx: orgChild1.orgChild1PhoneEx,
                orgTreePhoneIn: orgChild1.orgChild1PhoneIn,
                orgTreeFax: orgChild1.orgChild1Fax,
                orgRevisionId: orgRoot.orgRevisionId,
                orgRootName: orgRoot.orgRootName,
                responsibility: orgChild1.responsibility,
                isOfficer: orgChild1.isOfficer,
                isInformation: orgChild1.isInformation,
                labelName:
                  orgChild1.orgChild1Name +
                  " " +
                  orgRoot.orgRootCode +
                  orgChild1.orgChild1Code +
                  " " +
                  orgChild1.orgChild1ShortName +
                  "/" +
                  orgRoot.orgRootName +
                  " " +
                  orgRoot.orgRootCode +
                  "00" +
                  " " +
                  orgRoot.orgRootShortName,

                children: await Promise.all(
                  orgChild2Data
                    .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id)
                    .map(async (orgChild2) => ({
                      orgTreeId: orgChild2.id,
                      orgRootId: orgChild1.id,
                      orgLevel: 2,
                      misId: orgChild2.misId,
                      orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                      orgTreeName: orgChild2.orgChild2Name,
                      orgTreeShortName: orgChild2.orgChild2ShortName,
                      orgTreeCode: orgChild2.orgChild2Code,
                      orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code,
                      orgTreeRank: orgChild2.orgChild2Rank,
                      orgTreeRankSub: orgChild2.orgChild2RankSub,
                      DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE,
                      DIVISION_CODE: orgChild2.DIVISION_CODE,
                      SECTION_CODE: orgChild2.SECTION_CODE,
                      JOB_CODE: orgChild2.JOB_CODE,
                      orgTreeOrder: orgChild2.orgChild2Order,
                      orgRootCode: orgRoot.orgRootCode,
                      orgTreePhoneEx: orgChild2.orgChild2PhoneEx,
                      orgTreePhoneIn: orgChild2.orgChild2PhoneIn,
                      orgTreeFax: orgChild2.orgChild2Fax,
                      orgRevisionId: orgRoot.orgRevisionId,
                      orgRootName: orgRoot.orgRootName,
                      responsibility: orgChild2.responsibility,
                      labelName:
                        orgChild2.orgChild2Name +
                        " " +
                        orgRoot.orgRootCode +
                        orgChild2.orgChild2Code +
                        " " +
                        orgChild2.orgChild2ShortName +
                        "/" +
                        orgChild1.orgChild1Name +
                        " " +
                        orgRoot.orgRootCode +
                        orgChild1.orgChild1Code +
                        " " +
                        orgChild1.orgChild1ShortName +
                        "/" +
                        orgRoot.orgRootName +
                        " " +
                        orgRoot.orgRootCode +
                        "00" +
                        " " +
                        orgRoot.orgRootShortName,

                      children: await Promise.all(
                        orgChild3Data
                          .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id)
                          .map(async (orgChild3) => ({
                            orgTreeId: orgChild3.id,
                            orgRootId: orgChild2.id,
                            orgLevel: 3,
                            misId: orgChild3.misId,
                            orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                            orgTreeName: orgChild3.orgChild3Name,
                            orgTreeShortName: orgChild3.orgChild3ShortName,
                            orgTreeCode: orgChild3.orgChild3Code,
                            orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code,
                            orgTreeRank: orgChild3.orgChild3Rank,
                            orgTreeRankSub: orgChild3.orgChild3RankSub,
                            DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE,
                            DIVISION_CODE: orgChild3.DIVISION_CODE,
                            SECTION_CODE: orgChild3.SECTION_CODE,
                            JOB_CODE: orgChild3.JOB_CODE,
                            orgTreeOrder: orgChild3.orgChild3Order,
                            orgRootCode: orgRoot.orgRootCode,
                            orgTreePhoneEx: orgChild3.orgChild3PhoneEx,
                            orgTreePhoneIn: orgChild3.orgChild3PhoneIn,
                            orgTreeFax: orgChild3.orgChild3Fax,
                            orgRevisionId: orgRoot.orgRevisionId,
                            orgRootName: orgRoot.orgRootName,
                            responsibility: orgChild3.responsibility,
                            labelName:
                              orgChild3.orgChild3Name +
                              " " +
                              orgRoot.orgRootCode +
                              orgChild3.orgChild3Code +
                              " " +
                              orgChild3.orgChild3ShortName +
                              "/" +
                              orgChild2.orgChild2Name +
                              " " +
                              orgRoot.orgRootCode +
                              orgChild2.orgChild2Code +
                              " " +
                              orgChild2.orgChild2ShortName +
                              "/" +
                              orgChild1.orgChild1Name +
                              " " +
                              orgRoot.orgRootCode +
                              orgChild1.orgChild1Code +
                              " " +
                              orgChild1.orgChild1ShortName +
                              "/" +
                              orgRoot.orgRootName +
                              " " +
                              orgRoot.orgRootCode +
                              "00" +
                              " " +
                              orgRoot.orgRootShortName,

                            children: await Promise.all(
                              orgChild4Data
                                .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id)
                                .map(async (orgChild4) => ({
                                  orgTreeId: orgChild4.id,
                                  orgRootId: orgChild3.id,
                                  orgLevel: 4,
                                  misId: orgChild4.misId,
                                  orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                                  orgTreeName: orgChild4.orgChild4Name,
                                  orgTreeShortName: orgChild4.orgChild4ShortName,
                                  orgTreeCode: orgChild4.orgChild4Code,
                                  orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code,
                                  orgTreeRank: orgChild4.orgChild4Rank,
                                  orgTreeRankSub: orgChild4.orgChild4RankSub,
                                  DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE,
                                  DIVISION_CODE: orgChild4.DIVISION_CODE,
                                  SECTION_CODE: orgChild4.SECTION_CODE,
                                  JOB_CODE: orgChild4.JOB_CODE,
                                  orgTreeOrder: orgChild4.orgChild4Order,
                                  orgRootCode: orgRoot.orgRootCode,
                                  orgTreePhoneEx: orgChild4.orgChild4PhoneEx,
                                  orgTreePhoneIn: orgChild4.orgChild4PhoneIn,
                                  orgTreeFax: orgChild4.orgChild4Fax,
                                  orgRevisionId: orgRoot.orgRevisionId,
                                  orgRootName: orgRoot.orgRootName,
                                  responsibility: orgChild4.responsibility,
                                  labelName:
                                    orgChild4.orgChild4Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild4.orgChild4Code +
                                    " " +
                                    orgChild4.orgChild4ShortName +
                                    "/" +
                                    orgChild3.orgChild3Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild3.orgChild3Code +
                                    " " +
                                    orgChild3.orgChild3ShortName +
                                    "/" +
                                    orgChild2.orgChild2Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild2.orgChild2Code +
                                    " " +
                                    orgChild2.orgChild2ShortName +
                                    "/" +
                                    orgChild1.orgChild1Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild1.orgChild1Code +
                                    " " +
                                    orgChild1.orgChild1ShortName +
                                    "/" +
                                    orgRoot.orgRootName +
                                    " " +
                                    orgRoot.orgRootCode +
                                    "00" +
                                    " " +
                                    orgRoot.orgRootShortName,
                                })),
                            ),
                          })),
                      ),
                    })),
                ),
              })),
          ),
        };
      }),
    );

    // return new HttpSuccess({ remark: _revision.remark, data: formattedData });
    return new HttpSuccess({ remark: _revision.remark, data: formattedData });
  }

  /**
   * API ตั้งเวลาเผยแพร่
   *
   * @summary ORG_025 - ตั้งเวลาเผยแพร่ (ADMIN) #27
   *
   * @param {string} id Id revison
   */
  @Put("/set/publish/{id}")
  async Edit(
    @Path() id: string,
    @Body() requestBody: { orgPublishDate: Date },
    @Request() request: RequestWithUser,
  ) {
    // await new permission().PermissionUpdate(request, "SYS_ORG");//ไม่แน่ใจOFFปิดไว้ก่อน
    const orgRevision = await this.orgRevisionRepository.findOne({
      where: {
        id: id,
        orgRevisionIsDraft: true,
        orgRevisionIsCurrent: false,
      },
    });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. RevisionId");
    }
    const before = structuredClone(orgRevision);
    orgRevision.lastUpdateUserId = request.user.sub;
    orgRevision.lastUpdateFullName = request.user.name;
    orgRevision.lastUpdatedAt = new Date();
    orgRevision.orgPublishDate = requestBody.orgPublishDate;
    this.orgRevisionRepository.merge(orgRevision, requestBody);
    await this.orgRevisionRepository.save(orgRevision, { data: request });
    setLogDataDiff(request, { before, after: orgRevision });
    return new HttpSuccess();
  }

  /**
   *  API ประวัติหน่วยงาน
   *
   * @summary ORG_039 - ประวัติหน่วยงาน (ADMIN) #42
   *
   */
  @Post("/history/publish")
  async GetHistoryPublish(
    @Body()
    requestBody: {
      id: string;
      type: number;
    },
    @Request() request: RequestWithUser,
  ) {
    if (requestBody.type == 1) {
      const orgChild1 = await this.child1Repository.findOne({
        where: { id: requestBody.id },
      });
      if (!orgChild1) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child1");
      }
      const datas = await this.child1Repository
        .createQueryBuilder("child1")
        .where("child1.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild1.ancestorDNA })
        .andWhere("child1.ancestorDNA <> :nullUUID", {
          nullUUID: "00000000-0000-0000-0000-000000000000",
        })
        .andWhere("child1.ancestorDNA IS NOT NULL")
        .leftJoinAndSelect("child1.orgRevision", "orgRevision")
        .orderBy("child1.lastUpdatedAt", "DESC")
        .getMany();
      const _data = datas.map((item) => ({
        id: item.id,
        orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName,
        name: item.orgChild1Name,
        lastUpdatedAt: item.lastUpdatedAt,
      }));
      return new HttpSuccess(_data);
    } else if (requestBody.type == 2) {
      const orgChild2 = await this.child2Repository.findOne({
        where: { id: requestBody.id },
      });
      if (!orgChild2) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child2");
      }
      const datas = await this.child2Repository
        .createQueryBuilder("child2")
        .where("child2.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild2.ancestorDNA })
        .andWhere("child2.ancestorDNA <> :nullUUID", {
          nullUUID: "00000000-0000-0000-0000-000000000000",
        })
        .andWhere("child2.ancestorDNA IS NOT NULL")
        .leftJoinAndSelect("child2.orgRevision", "orgRevision")
        .orderBy("child2.lastUpdatedAt", "DESC")
        .getMany();
      const _data = datas.map((item) => ({
        id: item.id,
        orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName,
        name: item.orgChild2Name,
        lastUpdatedAt: item.lastUpdatedAt,
      }));
      return new HttpSuccess(_data);
    } else if (requestBody.type == 3) {
      const orgChild3 = await this.child3Repository.findOne({
        where: { id: requestBody.id },
      });
      if (!orgChild3) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child3");
      }
      const datas = await this.child3Repository
        .createQueryBuilder("child3")
        .where("child3.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild3.ancestorDNA })
        .andWhere("child3.ancestorDNA <> :nullUUID", {
          nullUUID: "00000000-0000-0000-0000-000000000000",
        })
        .andWhere("child3.ancestorDNA IS NOT NULL")
        .leftJoinAndSelect("child3.orgRevision", "orgRevision")
        .orderBy("child3.lastUpdatedAt", "DESC")
        .getMany();
      const _data = datas.map((item) => ({
        id: item.id,
        orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName,
        name: item.orgChild3Name,
        lastUpdatedAt: item.lastUpdatedAt,
      }));
      return new HttpSuccess(_data);
    } else if (requestBody.type == 4) {
      const orgChild4 = await this.child4Repository.findOne({
        where: { id: requestBody.id },
      });
      if (!orgChild4) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child4");
      }
      const datas = await this.child4Repository
        .createQueryBuilder("child4")
        .where("child4.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild4.ancestorDNA })
        .andWhere("child4.ancestorDNA <> :nullUUID", {
          nullUUID: "00000000-0000-0000-0000-000000000000",
        })
        .andWhere("child4.ancestorDNA IS NOT NULL")
        .leftJoinAndSelect("child4.orgRevision", "orgRevision")
        .orderBy("child4.lastUpdatedAt", "DESC")
        .getMany();
      const _data = datas.map((item) => ({
        id: item.id,
        orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName,
        name: item.orgChild4Name,
        lastUpdatedAt: item.lastUpdatedAt,
      }));
      return new HttpSuccess(_data);
    } else {
      const orgRoot = await this.orgRootRepository.findOne({
        where: { id: requestBody.id },
      });
      if (!orgRoot) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Root");
      }
      const datas = await this.orgRootRepository
        .createQueryBuilder("root")
        .where("root.ancestorDNA = :ancestorDNA", { ancestorDNA: orgRoot.ancestorDNA })
        .andWhere("root.ancestorDNA <> :nullUUID", {
          nullUUID: "00000000-0000-0000-0000-000000000000",
        })
        .andWhere("root.ancestorDNA IS NOT NULL")
        .leftJoinAndSelect("root.orgRevision", "orgRevision")
        .orderBy("root.lastUpdatedAt", "DESC")
        .getMany();
      const _data = datas.map((item) => ({
        id: item.id,
        orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName,
        name: item.orgRootName,
        lastUpdatedAt: item.lastUpdatedAt,
      }));
      return new HttpSuccess(_data);
    }
  }

  /**
   * API จัดลำดับโครงสร้าง
   *
   * @summary ORG_038 - จัดลำดับโครงสร้าง (ADMIN) #41
   *
   */
  @Post("sort")
  async Sort(
    @Body() requestBody: { id: string; type: number; sortId: string[] },
    @Request() request: RequestWithUser,
  ) {
    await new permission().PermissionUpdate(request, "SYS_ORG");
    const before = null;
    switch (requestBody.type) {
      case 0: {
        const revisionId = await this.orgRevisionRepository.findOne({
          where: { id: requestBody.id },
        });
        if (!revisionId?.id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revisionId: " + requestBody.id);
        }
        const listRootId = await this.orgRootRepository.find({
          where: { orgRevisionId: requestBody.id },
          select: ["id", "orgRootOrder"],
        });
        if (!listRootId) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId.");
        }
        const sortData = listRootId.map((data) => ({
          id: data.id,
          orgRootOrder: requestBody.sortId.indexOf(data.id) + 1,
        }));
        await this.orgRootRepository.save(sortData, { data: request });
        setLogDataDiff(request, { before, after: sortData });
        break;
      }

      case 1: {
        const rootId = await this.orgRootRepository.findOne({ where: { id: requestBody.id } });
        if (!rootId?.id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId: " + requestBody.id);
        }
        const listChild1Id = await this.child1Repository.find({
          where: { orgRootId: requestBody.id },
          select: ["id", "orgChild1Order"],
        });
        if (!listChild1Id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id.");
        }
        const sortData = listChild1Id.map((data) => ({
          id: data.id,
          orgChild1Order: requestBody.sortId.indexOf(data.id) + 1,
        }));
        await this.child1Repository.save(sortData, { data: request });
        setLogDataDiff(request, { before, after: sortData });
        break;
      }

      case 2: {
        const child1Id = await this.child1Repository.findOne({ where: { id: requestBody.id } });
        if (!child1Id?.id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id: " + requestBody.id);
        }
        const listChild2Id = await this.child2Repository.find({
          where: { orgChild1Id: requestBody.id },
          select: ["id", "orgChild2Order"],
        });
        if (!listChild2Id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id.");
        }
        const sortData = listChild2Id.map((data) => ({
          id: data.id,
          orgChild2Order: requestBody.sortId.indexOf(data.id) + 1,
        }));
        await this.child2Repository.save(sortData, { data: request });
        setLogDataDiff(request, { before, after: sortData });
        break;
      }

      case 3: {
        const child2Id = await this.child2Repository.findOne({ where: { id: requestBody.id } });
        if (!child2Id?.id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id: " + requestBody.id);
        }
        const listChild3Id = await this.child3Repository.find({
          where: { orgChild2Id: requestBody.id },
          select: ["id", "orgChild3Order"],
        });
        if (!listChild3Id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id.");
        }
        const sortData = listChild3Id.map((data) => ({
          id: data.id,
          orgChild3Order: requestBody.sortId.indexOf(data.id) + 1,
        }));
        await this.child3Repository.save(sortData, { data: request });
        setLogDataDiff(request, { before, after: sortData });
        break;
      }

      case 4: {
        const child3Id = await this.child3Repository.findOne({ where: { id: requestBody.id } });
        if (!child3Id?.id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id: " + requestBody.id);
        }
        const listChild4Id = await this.child4Repository.find({
          where: { orgChild3Id: requestBody.id },
          select: ["id", "orgChild4Order"],
        });
        if (!listChild4Id) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4Id.");
        }
        const sortData = listChild4Id.map((data) => ({
          id: data.id,
          orgChild4Order: requestBody.sortId.indexOf(data.id) + 1,
        }));
        await this.child4Repository.save(sortData, { data: request });
        setLogDataDiff(request, { before, after: sortData });
        break;
      }

      default:
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.type);
    }
    return new HttpSuccess();
  }

  /**
   * API เผยแพร่ข้อมูล
   *
   * @summary ORG_071 - เผยแพร่ข้อมูล (ADMIN) #57
   *
   * @param {string} id Id revison
   */
  @Get("get/publish")
  async runPublish(@Request() request: RequestWithUser) {
    try {
      // CheckQueueInProgress
      // console.log("🚀 ตรวจสอบว่ามีงานอยู่ในคิว");
      const [isBusyDraft, isBusyPublish] = await Promise.all([
        checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`),
        checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`),
      ]);
      // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft);
      // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish);
      if (isBusyDraft || isBusyPublish) {
        // console.log("🚫 พบว่ามีงานอยู่ในคิว — error")
        throw new HttpError(
          HttpStatusCode.CONFLICT,
          "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน",
        );
      }

      const today = new Date();
      today.setHours(0, 0, 0, 0); // Set time to the beginning of the day

      const orgRevisionDraft = await this.orgRevisionRepository
        .createQueryBuilder("orgRevision")
        .where("orgRevision.orgRevisionIsDraft = true")
        .andWhere("orgRevision.orgRevisionIsCurrent = false")
        //   .andWhere("DATE(orgRevision.orgPublishDate) = :today", { today })
        .getOne();
      if (!orgRevisionDraft) {
        return new HttpSuccess();
        //   throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่มีข้อมูลเผยแพร่");
      }
      // if (orgRevisionPublish) {
      //   orgRevisionPublish.orgRevisionIsDraft = false;
      //   orgRevisionPublish.orgRevisionIsCurrent = false;
      //   await this.orgRevisionRepository.save(orgRevisionPublish);
      // }
      // orgRevisionDraft.orgRevisionIsCurrent = true;
      // orgRevisionDraft.orgRevisionIsDraft = false;
      // await this.orgRevisionRepository.save(orgRevisionDraft);
      const msg = {
        data: {
          id: orgRevisionDraft.id,
          status: "NOW",
          lastUpdateUserId: request.user.sub,
          lastUpdateFullName: request.user.name,
          lastUpdatedAt: new Date(),
        },
        user: request.user,
        token: request.headers["authorization"],
      };
      await sendToQueueOrg(msg);
      return new HttpSuccess();
    } catch (error: any) {
      console.error("Error publishing draft organization:", error);
      throw error;
    }
  }

  /**
   * Cronjob
   */
  async cronjobRevision() {
    console.log("[CronJob] cronjobRevision START");
    const startTime = Date.now();

    const today = new Date();
    today.setUTCHours(0, 0, 0, 0); // Set time to the beginning of the day
    const tomorrow = new Date(today);
    tomorrow.setDate(tomorrow.getDate() + 1);

    console.log(
      `[CronJob] Searching for draft revision with publishDate between ${today.toISOString()} and ${tomorrow.toISOString()}`,
    );

    const orgRevisionDraft = await this.orgRevisionRepository
      .createQueryBuilder("orgRevision")
      .where("orgRevision.orgRevisionIsDraft = true")
      .andWhere("orgRevision.orgRevisionIsCurrent = false")
      .andWhere("orgRevision.orgPublishDate BETWEEN :today AND :tomorrow", { today, tomorrow })
      .getOne();

    if (!orgRevisionDraft) {
      console.log("[CronJob] No draft revision found to publish");
      return new HttpSuccess();
    }

    console.log(
      `[CronJob] Found draft revision: ${orgRevisionDraft.id}, name: ${orgRevisionDraft.orgRevisionName}, publishDate: ${orgRevisionDraft.orgPublishDate}`,
    );

    // if (orgRevisionPublish) {
    //   orgRevisionPublish.orgRevisionIsDraft = false;
    //   orgRevisionPublish.orgRevisionIsCurrent = false;
    //   await this.orgRevisionRepository.save(orgRevisionPublish);
    // }
    // orgRevisionDraft.orgRevisionIsCurrent = true;
    // orgRevisionDraft.orgRevisionIsDraft = false;
    // await this.orgRevisionRepository.save(orgRevisionDraft);

    // const posMaster = await this.posMasterRepository.find({
    //   where: { orgRevisionId: orgRevisionDraft.id },
    // });
    // posMaster.forEach(async (item) => {
    //   // if(item.next_holderId != null){
    //   item.current_holderId = item.next_holderId;
    //   item.next_holderId = null;
    //   await this.posMasterRepository.save(item);
    //   // }
    // });
    const msg = {
      data: {
        id: orgRevisionDraft.id,
        status: "ON_SCHEDULE",
        lastUpdateUserId: "system",
        lastUpdateFullName: "system",
        lastUpdatedAt: new Date(),
      },
    };

    console.log(`[CronJob] Sending to RabbitMQ queue - revisionId: ${orgRevisionDraft.id}`);
    sendToQueueOrg(msg);
    console.log(`[CronJob] Sent to queue successfully - Total time: ${Date.now() - startTime}ms`);
    return new HttpSuccess();
  }

  /**
   * API Organizational Chart
   *
   * @summary Organizational Chart
   *
   * @param {string} revisionId Id revison
   */
  @Get("org-chart/{revisionId}")
  async orgchart(@Path() revisionId: string) {
    const data = await this.orgRevisionRepository.findOne({
      where: { id: revisionId },
    });
    if (!data) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้าง");
    }
    let posMasterRoot: any;
    let posMasterChild1: any;
    let posMasterChild2: any;
    let posMasterChild3: any;
    let posMasterChild4: any;
    if (data.orgRevisionIsCurrent == true && data.orgRevisionIsDraft == false) {
      // ใช้ query เดียวแทน 5 queries แยก เพื่อความเร็วในการทำงาน
      const allPosMasters = await this.posMasterRepository.find({
        where: {
          orgRevisionId: data.id,
        },
        relations: [
          "current_holder",
          "orgRoot",
          "orgChild1",
          "orgChild2",
          "orgChild3",
          "orgChild4",
        ],
        order: {
          orgRoot: {
            orgRootOrder: "ASC",
          },
          posMasterOrder: "ASC",
          posMasterNo: "ASC",
        },
      });

      // แยกข้อมูลด้วย JavaScript แทนการใช้ database queries หลาย ๆ ครั้ง
      posMasterRoot = allPosMasters.filter((item) => item.orgChild1Id === null);
      posMasterChild1 = allPosMasters.filter(
        (item) => item.orgChild2Id === null && item.orgChild1Id !== null,
      );
      posMasterChild2 = allPosMasters.filter(
        (item) => item.orgChild3Id === null && item.orgChild2Id !== null,
      );
      posMasterChild3 = allPosMasters.filter(
        (item) => item.orgChild4Id === null && item.orgChild3Id !== null,
      );
      posMasterChild4 = allPosMasters.filter((item) => item.orgChild4Id !== null);

      // สร้าง Maps เพื่อ lookup ที่เร็วขึ้น แทนการใช้ filter หลายรอบ
      const rootByOrgRootId = new Map<string, PosMaster[]>();
      const child1ByOrgRootId = new Map<string, PosMaster[]>();
      const child1ByOrgChild1Id = new Map<string, PosMaster[]>();
      const child2ByOrgChild1Id = new Map<string, PosMaster[]>();
      const child2ByOrgChild2Id = new Map<string, PosMaster[]>();
      const child3ByOrgChild2Id = new Map<string, PosMaster[]>();
      const child3ByOrgChild3Id = new Map<string, PosMaster[]>();
      const child4ByOrgChild3Id = new Map<string, PosMaster[]>();

      // Pre-compute all lookups เพื่อหลีกเลี่ยงการ filter ซ้ำๆ
      posMasterRoot.forEach((item: PosMaster) => {
        if (item.current_holderId) {
          if (item.isDirector) {
            // Root directors will be processed in main loop
          } else {
            // Group root non-directors by orgRootId
            const key = item.orgRootId || "";
            if (!rootByOrgRootId.has(key)) rootByOrgRootId.set(key, []);
            rootByOrgRootId.get(key)!.push(item);
          }
        }
      });

      posMasterChild1.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child1 directors by orgRootId
          const key = item.orgRootId || "";
          if (!child1ByOrgRootId.has(key)) child1ByOrgRootId.set(key, []);
          child1ByOrgRootId.get(key)!.push(item);
        } else if (item.current_holderId) {
          // Group child1 non-directors by orgChild1Id
          const key = item.orgChild1Id || "";
          if (!child1ByOrgChild1Id.has(key)) child1ByOrgChild1Id.set(key, []);
          child1ByOrgChild1Id.get(key)!.push(item);
        }
      });

      posMasterChild2.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child2 directors by orgChild1Id
          const key = item.orgChild1Id || "";
          if (!child2ByOrgChild1Id.has(key)) child2ByOrgChild1Id.set(key, []);
          child2ByOrgChild1Id.get(key)!.push(item);
        } else if (item.current_holderId) {
          // Group child2 non-directors by orgChild2Id
          const key = item.orgChild2Id || "";
          if (!child2ByOrgChild2Id.has(key)) child2ByOrgChild2Id.set(key, []);
          child2ByOrgChild2Id.get(key)!.push(item);
        }
      });

      posMasterChild3.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child3 directors by orgChild2Id
          const key = item.orgChild2Id || "";
          if (!child3ByOrgChild2Id.has(key)) child3ByOrgChild2Id.set(key, []);
          child3ByOrgChild2Id.get(key)!.push(item);
        } else if (item.current_holderId) {
          // Group child3 non-directors by orgChild3Id
          const key = item.orgChild3Id || "";
          if (!child3ByOrgChild3Id.has(key)) child3ByOrgChild3Id.set(key, []);
          child3ByOrgChild3Id.get(key)!.push(item);
        }
      });

      posMasterChild4.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child4 directors by orgChild3Id
          const key = item.orgChild3Id || "";
          if (!child4ByOrgChild3Id.has(key)) child4ByOrgChild3Id.set(key, []);
          child4ByOrgChild3Id.get(key)!.push(item);
        }
      });

      // Helper function เพื่อสร้าง node object
      const createNode = (item: PosMaster, level: number, holder: any, orgInfo: any) => ({
        level,
        personID: holder?.id ?? "",
        name: holder ? `${holder.firstName} ${holder.lastName}` : "ว่าง",
        avatar:
          holder?.avatar && holder?.avatarName ? `${holder.avatar}/${holder.avatarName}` : null,
        positionName: holder?.position ?? "",
        positionNum: `${orgInfo.shortName} ${item.posMasterNo}`,
        positionNumInt: item.posMasterNo,
        departmentName: orgInfo.name,
        organizationId: orgInfo.id,
        children: [] as any[],
      });

      let formattedData = posMasterRoot
        .filter((x: any) => x.current_holderId != null && x.isDirector)
        .map((x0: PosMaster) => {
          const childLevel1: any[] = [];

          // Add root non-directors (Level 2)
          const rootNonDirectors = rootByOrgRootId.get(x0.orgRootId || "") || [];
          rootNonDirectors.forEach((x00: PosMaster) => {
            childLevel1.push(
              createNode(x00, 2, x00.current_holder, {
                shortName: x00.orgRoot.orgRootShortName,
                name: x00.orgRoot.orgRootName,
                id: x00.orgRoot.id,
              }),
            );
          });

          // Add child1 directors (Level 2)
          const child1Directors = child1ByOrgRootId.get(x0.orgRootId || "") || [];
          child1Directors.forEach((x1: PosMaster) => {
            const childLevel2: any[] = [];

            // Add child1 non-directors (Level 3)
            const child1NonDirectors = child1ByOrgChild1Id.get(x1.orgChild1Id || "") || [];
            child1NonDirectors.forEach((x11: PosMaster) => {
              childLevel2.push(
                createNode(x11, 3, x11.current_holder, {
                  shortName: x11.orgChild1.orgChild1ShortName,
                  name: x11.orgChild1.orgChild1Name,
                  id: x11.orgChild1.id,
                }),
              );
            });

            // Add child2 directors (Level 3)
            const child2Directors = child2ByOrgChild1Id.get(x1.orgChild1Id || "") || [];
            child2Directors.forEach((x2: PosMaster) => {
              const childLevel3: any[] = [];

              // Add child2 non-directors (Level 4)
              const child2NonDirectors = child2ByOrgChild2Id.get(x2.orgChild2Id || "") || [];
              child2NonDirectors.forEach((x22: PosMaster) => {
                childLevel3.push(
                  createNode(x22, 4, x22.current_holder, {
                    shortName: x22.orgChild2.orgChild2ShortName,
                    name: x22.orgChild2.orgChild2Name,
                    id: x22.orgChild2.id,
                  }),
                );
              });

              // Add child3 directors (Level 4)
              const child3Directors = child3ByOrgChild2Id.get(x2.orgChild2Id || "") || [];
              child3Directors.forEach((x3: PosMaster) => {
                const childLevel4: any[] = [];

                // Add child3 non-directors (Level 5)
                const child3NonDirectors = child3ByOrgChild3Id.get(x3.orgChild3Id || "") || [];
                child3NonDirectors.forEach((x33: PosMaster) => {
                  childLevel4.push(
                    createNode(x33, 5, x33.current_holder, {
                      shortName: x33.orgChild3.orgChild3ShortName,
                      name: x33.orgChild3.orgChild3Name,
                      id: x33.orgChild3.id,
                    }),
                  );
                });

                // Add child4 directors (Level 5)
                const child4Directors = child4ByOrgChild3Id.get(x3.orgChild3Id || "") || [];
                child4Directors.forEach((x4: PosMaster) => {
                  const childLevel5: any[] = [];

                  // Add child4 non-directors (Level 5)
                  posMasterChild4
                    .filter(
                      (x: PosMaster) =>
                        !x.isDirector && x.current_holderId && x.orgChild3Id === x4.orgChild3Id,
                    )
                    .forEach((x44: PosMaster) => {
                      childLevel5.push(
                        createNode(x44, 5, x44.current_holder, {
                          shortName: x44.orgChild4.orgChild4ShortName,
                          name: x44.orgChild4.orgChild4Name,
                          id: x44.orgChild4.id,
                        }),
                      );
                    });

                  const child4Node = createNode(x4, 4, x4.current_holder, {
                    shortName: x4.orgChild4.orgChild4ShortName,
                    name: x4.orgChild4.orgChild4Name,
                    id: x4.orgChild4.id,
                  });
                  child4Node.children = childLevel5;
                  childLevel4.push(child4Node);
                });

                const child3Node = createNode(x3, 4, x3.current_holder, {
                  shortName: x3.orgChild3.orgChild3ShortName,
                  name: x3.orgChild3.orgChild3Name,
                  id: x3.orgChild3.id,
                });
                child3Node.children = childLevel4;
                childLevel3.push(child3Node);
              });

              const child2Node = createNode(x2, 3, x2.current_holder, {
                shortName: x2.orgChild2.orgChild2ShortName,
                name: x2.orgChild2.orgChild2Name,
                id: x2.orgChild2.id,
              });
              child2Node.children = childLevel3;
              childLevel2.push(child2Node);
            });

            const child1Node = createNode(x1, 2, x1.current_holder, {
              shortName: x1.orgChild1.orgChild1ShortName,
              name: x1.orgChild1.orgChild1Name,
              id: x1.orgChild1.id,
            });
            child1Node.children = childLevel2;
            childLevel1.push(child1Node);
          });

          // Root Level 1
          const rootNode = createNode(x0, 1, x0.current_holder, {
            shortName: x0.orgRoot.orgRootShortName,
            name: x0.orgRoot.orgRootName,
            id: x0.orgRoot.id,
          });
          rootNode.children = childLevel1;
          return rootNode;
        });

      const formattedData_ = {
        personID: "",
        name: "",
        avatar: "",
        positionName: "",
        positionNum: "",
        positionNumInt: null,
        departmentName: data.orgRevisionName,
        organizationId: data.id,
        children: formattedData,
      };
      return new HttpSuccess([formattedData_]);
    } else if (data.orgRevisionIsCurrent == false && data.orgRevisionIsDraft == true) {
      // ใช้ query เดียวแทน 5 queries แยก เพื่อความเร็วในการทำงาน
      const allPosMasters = await this.posMasterRepository.find({
        where: {
          orgRevisionId: data.id,
          // next_holderId: Not(IsNull()),
        },
        relations: ["next_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"],
      });

      // แยกข้อมูลด้วย JavaScript แทนการใช้ database queries หลาย ๆ ครั้ง
      posMasterRoot = allPosMasters.filter((item) => item.orgChild1Id === null);
      posMasterChild1 = allPosMasters.filter(
        (item) => item.orgChild2Id === null && item.orgChild1Id !== null,
      );
      posMasterChild2 = allPosMasters.filter(
        (item) => item.orgChild3Id === null && item.orgChild2Id !== null,
      );
      posMasterChild3 = allPosMasters.filter(
        (item) => item.orgChild4Id === null && item.orgChild3Id !== null,
      );
      posMasterChild4 = allPosMasters.filter((item) => item.orgChild4Id !== null);

      // สร้าง Maps เพื่อ lookup ที่เร็วขึ้น แทนการใช้ filter หลายรอบ สำหรับ draft version
      const rootByOrgRootIdDraft = new Map<string, PosMaster[]>();
      const child1ByOrgRootIdDraft = new Map<string, PosMaster[]>();
      const child1ByOrgChild1IdDraft = new Map<string, PosMaster[]>();
      const child2ByOrgChild1IdDraft = new Map<string, PosMaster[]>();
      const child2ByOrgChild2IdDraft = new Map<string, PosMaster[]>();
      const child3ByOrgChild2IdDraft = new Map<string, PosMaster[]>();
      const child3ByOrgChild3IdDraft = new Map<string, PosMaster[]>();
      const child4ByOrgChild3IdDraft = new Map<string, PosMaster[]>();

      // Pre-compute all lookups เพื่อหลีกเลี่ยงการ filter ซ้ำๆ สำหรับ draft version
      posMasterRoot.forEach((item: PosMaster) => {
        if (item.next_holderId) {
          if (item.isDirector) {
            // Root directors will be processed in main loop
          } else {
            // Group root non-directors by orgRootId
            const key = item.orgRootId || "";
            if (!rootByOrgRootIdDraft.has(key)) rootByOrgRootIdDraft.set(key, []);
            rootByOrgRootIdDraft.get(key)!.push(item);
          }
        }
      });

      posMasterChild1.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child1 directors by orgRootId
          const key = item.orgRootId || "";
          if (!child1ByOrgRootIdDraft.has(key)) child1ByOrgRootIdDraft.set(key, []);
          child1ByOrgRootIdDraft.get(key)!.push(item);
        } else if (item.next_holderId) {
          // Group child1 non-directors by orgChild1Id
          const key = item.orgChild1Id || "";
          if (!child1ByOrgChild1IdDraft.has(key)) child1ByOrgChild1IdDraft.set(key, []);
          child1ByOrgChild1IdDraft.get(key)!.push(item);
        }
      });

      posMasterChild2.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child2 directors by orgChild1Id
          const key = item.orgChild1Id || "";
          if (!child2ByOrgChild1IdDraft.has(key)) child2ByOrgChild1IdDraft.set(key, []);
          child2ByOrgChild1IdDraft.get(key)!.push(item);
        } else if (item.next_holderId) {
          // Group child2 non-directors by orgChild2Id
          const key = item.orgChild2Id || "";
          if (!child2ByOrgChild2IdDraft.has(key)) child2ByOrgChild2IdDraft.set(key, []);
          child2ByOrgChild2IdDraft.get(key)!.push(item);
        }
      });

      posMasterChild3.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child3 directors by orgChild2Id
          const key = item.orgChild2Id || "";
          if (!child3ByOrgChild2IdDraft.has(key)) child3ByOrgChild2IdDraft.set(key, []);
          child3ByOrgChild2IdDraft.get(key)!.push(item);
        } else if (item.next_holderId) {
          // Group child3 non-directors by orgChild3Id
          const key = item.orgChild3Id || "";
          if (!child3ByOrgChild3IdDraft.has(key)) child3ByOrgChild3IdDraft.set(key, []);
          child3ByOrgChild3IdDraft.get(key)!.push(item);
        }
      });

      posMasterChild4.forEach((item: PosMaster) => {
        if (item.isDirector) {
          // Group child4 directors by orgChild3Id
          const key = item.orgChild3Id || "";
          if (!child4ByOrgChild3IdDraft.has(key)) child4ByOrgChild3IdDraft.set(key, []);
          child4ByOrgChild3IdDraft.get(key)!.push(item);
        }
      });

      // Helper function เพื่อสร้าง node object สำหรับ draft version
      const createNodeDraft = (item: PosMaster, level: number, holder: any, orgInfo: any) => ({
        level,
        personID: holder?.id ?? "",
        name: holder ? `${holder.firstName} ${holder.lastName}` : "ว่าง",
        avatar:
          holder?.avatar && holder?.avatarName ? `${holder.avatar}/${holder.avatarName}` : null,
        positionName: holder?.position ?? "",
        positionNum: `${orgInfo.shortName} ${item.posMasterNo}`,
        positionNumInt: item.posMasterNo,
        departmentName: orgInfo.name,
        organizationId: orgInfo.id,
        children: [] as any[],
      });

      let formattedData = posMasterRoot
        .filter((x: any) => x.next_holderId != null && x.isDirector)
        .map((x0: PosMaster) => {
          const childLevel1: any[] = [];

          // Add root non-directors (Level 2)
          const rootNonDirectors = rootByOrgRootIdDraft.get(x0.orgRootId || "") || [];
          rootNonDirectors.forEach((x00: PosMaster) => {
            childLevel1.push(
              createNodeDraft(x00, 2, x00.next_holder, {
                shortName: x00.orgRoot.orgRootShortName,
                name: x00.orgRoot.orgRootName,
                id: x00.orgRoot.id,
              }),
            );
          });

          // Add child1 directors (Level 2)
          const child1Directors = child1ByOrgRootIdDraft.get(x0.orgRootId || "") || [];
          child1Directors.forEach((x1: PosMaster) => {
            const childLevel2: any[] = [];

            // Add child1 non-directors (Level 3)
            const child1NonDirectors = child1ByOrgChild1IdDraft.get(x1.orgChild1Id || "") || [];
            child1NonDirectors.forEach((x11: PosMaster) => {
              childLevel2.push(
                createNodeDraft(x11, 3, x11.next_holder, {
                  shortName: x11.orgChild1.orgChild1ShortName,
                  name: x11.orgChild1.orgChild1Name,
                  id: x11.orgChild1.id,
                }),
              );
            });

            // Add child2 directors (Level 3)
            const child2Directors = child2ByOrgChild1IdDraft.get(x1.orgChild1Id || "") || [];
            child2Directors.forEach((x2: PosMaster) => {
              const childLevel3: any[] = [];

              // Add child2 non-directors (Level 4)
              const child2NonDirectors = child2ByOrgChild2IdDraft.get(x2.orgChild2Id || "") || [];
              child2NonDirectors.forEach((x22: PosMaster) => {
                childLevel3.push(
                  createNodeDraft(x22, 4, x22.next_holder, {
                    shortName: x22.orgChild2.orgChild2ShortName,
                    name: x22.orgChild2.orgChild2Name,
                    id: x22.orgChild2.id,
                  }),
                );
              });

              // Add child3 directors (Level 4)
              const child3Directors = child3ByOrgChild2IdDraft.get(x2.orgChild2Id || "") || [];
              child3Directors.forEach((x3: PosMaster) => {
                const childLevel4: any[] = [];

                // Add child3 non-directors (Level 5)
                const child3NonDirectors = child3ByOrgChild3IdDraft.get(x3.orgChild3Id || "") || [];
                child3NonDirectors.forEach((x33: PosMaster) => {
                  childLevel4.push(
                    createNodeDraft(x33, 5, x33.next_holder, {
                      shortName: x33.orgChild3.orgChild3ShortName,
                      name: x33.orgChild3.orgChild3Name,
                      id: x33.orgChild3.id,
                    }),
                  );
                });

                // Add child4 directors (Level 5)
                const child4Directors = child4ByOrgChild3IdDraft.get(x3.orgChild3Id || "") || [];
                child4Directors.forEach((x4: PosMaster) => {
                  const childLevel5: any[] = [];

                  // Add child4 non-directors (Level 5)
                  posMasterChild4
                    .filter(
                      (x: PosMaster) =>
                        !x.isDirector && x.next_holderId && x.orgChild3Id === x4.orgChild3Id,
                    )
                    .forEach((x44: PosMaster) => {
                      childLevel5.push(
                        createNodeDraft(x44, 5, x44.next_holder, {
                          shortName: x44.orgChild4.orgChild4ShortName,
                          name: x44.orgChild4.orgChild4Name,
                          id: x44.orgChild4.id,
                        }),
                      );
                    });

                  const child4Node = createNodeDraft(x4, 4, x4.next_holder, {
                    shortName: x4.orgChild4.orgChild4ShortName,
                    name: x4.orgChild4.orgChild4Name,
                    id: x4.orgChild4.id,
                  });
                  child4Node.children = childLevel5;
                  childLevel4.push(child4Node);
                });

                const child3Node = createNodeDraft(x3, 4, x3.next_holder, {
                  shortName: x3.orgChild3.orgChild3ShortName,
                  name: x3.orgChild3.orgChild3Name,
                  id: x3.orgChild3.id,
                });
                child3Node.children = childLevel4;
                childLevel3.push(child3Node);
              });

              const child2Node = createNodeDraft(x2, 3, x2.next_holder, {
                shortName: x2.orgChild2.orgChild2ShortName,
                name: x2.orgChild2.orgChild2Name,
                id: x2.orgChild2.id,
              });
              child2Node.children = childLevel3;
              childLevel2.push(child2Node);
            });

            const child1Node = createNodeDraft(x1, 2, x1.next_holder, {
              shortName: x1.orgChild1.orgChild1ShortName,
              name: x1.orgChild1.orgChild1Name,
              id: x1.orgChild1.id,
            });
            child1Node.children = childLevel2;
            childLevel1.push(child1Node);
          });

          // Root Level 1
          const rootNode = createNodeDraft(x0, 1, x0.next_holder, {
            shortName: x0.orgRoot.orgRootShortName,
            name: x0.orgRoot.orgRootName,
            id: x0.orgRoot.id,
          });
          rootNode.children = childLevel1;
          return rootNode;
        });

      const formattedData_ = {
        personID: "",
        name: "",
        avatar: "",
        positionName: "",
        positionNum: "",
        positionNumInt: null,
        departmentName: data.orgRevisionName,
        organizationId: data.id,
        children: formattedData,
      };
      return new HttpSuccess([formattedData_]);
    } else {
      return new HttpSuccess([
        {
          personID: "",
          name: "",
          avatar: "",
          positionName: "",
          positionNum: "",
          positionNumInt: null,
          departmentName: data.orgRevisionName,
          organizationId: data.id,
          children: [],
        },
      ]);
    }
  }

  /**
   * API Organizational Chart
   *
   * @summary Organizational Chart
   *
   * @param {string} revisionId Id revison
   */
  @Get("org-chart/{revisionId}/{rootId}")
  async orgChartByRoot(@Path() revisionId: string, @Path() rootId: string) {
    // get revision data
    const data = await this.orgRevisionRepository.findOne({ where: { id: revisionId } });
    if (!data) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้าง");
    }

    type OrgInfo = { shortName: string; name: string; id: string };

    let fieldId: "current_holderId" | "next_holderId" = "current_holderId";
    let relations: string[] =
      rootId === "root"
        ? ["current_holder", "orgRoot"]
        : ["current_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"];
    if (data.orgRevisionIsCurrent === false && data.orgRevisionIsDraft === true) {
      fieldId = "next_holderId";
      relations =
        rootId === "root"
          ? ["next_holder", "orgRoot"]
          : ["next_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"];
    }

    const where =
      rootId === "root"
        ? { orgRevisionId: data.id, posMasterNo: 1 }
        : { orgRevisionId: data.id, orgRootId: rootId };

    const allPosMasters = await this.posMasterRepository.find({
      where,
      relations,
      order: {
        orgRoot: { orgRootOrder: "ASC" },
        posMasterOrder: "ASC",
        posMasterNo: "ASC",
      },
    });

    // Split positions by level
    const posMasterRoot = allPosMasters.filter((item) => item.orgChild1Id === null);
    const posMasterChild1 =
      rootId !== "root"
        ? allPosMasters.filter((item) => item.orgChild2Id === null && item.orgChild1Id !== null)
        : [];
    const posMasterChild2 =
      rootId !== "root"
        ? allPosMasters.filter((item) => item.orgChild3Id === null && item.orgChild2Id !== null)
        : [];
    const posMasterChild3 =
      rootId !== "root"
        ? allPosMasters.filter((item) => item.orgChild4Id === null && item.orgChild3Id !== null)
        : [];
    const posMasterChild4 =
      rootId !== "root" ? allPosMasters.filter((item) => item.orgChild4Id !== null) : [];

    // Find the minimum posMasterNo for each orgRootId
    const minPosMasterNoByOrgRootId = new Map<string, number>();
    posMasterRoot.forEach((x) => {
      const orgRootId = x.orgRootId ?? "";
      const currentMin = minPosMasterNoByOrgRootId.get(orgRootId);
      if (
        (currentMin === undefined || x.posMasterNo < currentMin) &&
        (x as any)[fieldId] != null &&
        x.isDirector
      ) {
        minPosMasterNoByOrgRootId.set(orgRootId, x.posMasterNo);
      }
    });

    // Utility to group by key
    function groupBy<T>(
      arr: T[],
      keyFn: (item: T) => string | number | undefined | null,
    ): Map<string, T[]> {
      const map = new Map<string, T[]>();
      for (const item of arr) {
        let key = keyFn(item);
        if (key === undefined || key === null) key = "";
        key = String(key);
        if (!map.has(key)) map.set(key, []);
        map.get(key)!.push(item);
      }
      return map;
    }

    // Build lookup maps
    const rootByOrgRootId = groupBy(
      posMasterRoot.filter(
        (item) =>
          ((item as any)[fieldId] && !item.isDirector) ||
          (item.isDirector &&
            minPosMasterNoByOrgRootId.get(item.orgRootId ?? "") !== item.posMasterNo),
      ),
      (item) => item.orgRootId ?? "",
    );
    const child1ByOrgRootId = groupBy(
      posMasterChild1.filter((item) => item.isDirector),
      (item) => item.orgRootId ?? "",
    );
    const child1ByOrgChild1Id = groupBy(
      posMasterChild1.filter((item) => (item as any)[fieldId] && !item.isDirector),
      (item) => item.orgChild1Id ?? "",
    );
    const child2ByOrgChild1Id = groupBy(
      posMasterChild2.filter((item) => item.isDirector),
      (item) => item.orgChild1Id ?? "",
    );
    const child2ByOrgChild2Id = groupBy(
      posMasterChild2.filter((item) => (item as any)[fieldId] && !item.isDirector),
      (item) => item.orgChild2Id ?? "",
    );
    const child3ByOrgChild2Id = groupBy(
      posMasterChild3.filter((item) => item.isDirector),
      (item) => item.orgChild2Id ?? "",
    );
    const child3ByOrgChild3Id = groupBy(
      posMasterChild3.filter((item) => (item as any)[fieldId] && !item.isDirector),
      (item) => item.orgChild3Id ?? "",
    );
    const child4ByOrgChild3Id = groupBy(
      posMasterChild4.filter((item) => item.isDirector),
      (item) => item.orgChild3Id ?? "",
    );

    // Helper to create node
    function createNode(item: any, level: number, orgInfo: OrgInfo): any {
      const holder = fieldId === "current_holderId" ? item.current_holder : item.next_holder;
      return {
        level,
        personID: holder?.id ?? "",
        name: holder ? `${holder.firstName} ${holder.lastName}` : "ว่าง",
        avatar:
          holder?.avatar && holder?.avatarName ? `${holder.avatar}/${holder.avatarName}` : null,
        positionName: holder?.position ?? "",
        positionNum: `${orgInfo.shortName} ${item.posMasterNo}`,
        positionNumInt: item.posMasterNo,
        departmentName: orgInfo.name,
        organizationId: orgInfo.id,
        children: [],
      };
    }

    // Recursive builder for children
    function buildChildren(level: number, parent: any, orgInfo: OrgInfo): any[] {
      if (level === 2) {
        // Level 2: child1 non-directors and child2 directors
        const children: any[] = [];
        // Root non-directors
        (
          rootByOrgRootId
            .get(parent.orgRootId)
            ?.filter(
              (x00) =>
                !x00.isDirector ||
                (x00.isDirector &&
                  minPosMasterNoByOrgRootId.get(x00.orgRootId ?? "") !== x00.posMasterNo),
            ) || []
        ).forEach((x00) => {
          children.push(
            createNode(x00, 2, {
              shortName: x00.orgRoot.orgRootShortName,
              name: x00.orgRoot.orgRootName,
              id: x00.orgRoot.id,
            }),
          );
        });
        if (rootId !== "root") {
          // Child1 directors
          (child1ByOrgRootId.get(parent.orgRootId) || []).forEach((x1) => {
            const childLevel2 = buildChildren(3, x1, {
              shortName: x1.orgChild1.orgChild1ShortName,
              name: x1.orgChild1.orgChild1Name,
              id: x1.orgChild1.id,
            });
            const node = createNode(x1, 2, {
              shortName: x1.orgChild1.orgChild1ShortName,
              name: x1.orgChild1.orgChild1Name,
              id: x1.orgChild1.id,
            });
            node.children = childLevel2;
            children.push(node);
          });
        }
        return children;
      } else if (level === 3) {
        // Level 3: child1 non-directors and child2 directors
        const children: any[] = [];
        (child1ByOrgChild1Id.get(parent.orgChild1Id) || []).forEach((x11) => {
          children.push(
            createNode(x11, 3, {
              shortName: x11.orgChild1.orgChild1ShortName,
              name: x11.orgChild1.orgChild1Name,
              id: x11.orgChild1.id,
            }),
          );
        });
        (child2ByOrgChild1Id.get(parent.orgChild1Id) || []).forEach((x2) => {
          const childLevel3 = buildChildren(4, x2, {
            shortName: x2.orgChild2.orgChild2ShortName,
            name: x2.orgChild2.orgChild2Name,
            id: x2.orgChild2.id,
          });
          const node = createNode(x2, 3, {
            shortName: x2.orgChild2.orgChild2ShortName,
            name: x2.orgChild2.orgChild2Name,
            id: x2.orgChild2.id,
          });
          node.children = childLevel3;
          children.push(node);
        });
        return children;
      } else if (level === 4) {
        // Level 4: child2 non-directors and child3 directors
        const children: any[] = [];
        (child2ByOrgChild2Id.get(parent.orgChild2Id) || []).forEach((x22) => {
          children.push(
            createNode(x22, 4, {
              shortName: x22.orgChild2.orgChild2ShortName,
              name: x22.orgChild2.orgChild2Name,
              id: x22.orgChild2.id,
            }),
          );
        });
        (child3ByOrgChild2Id.get(parent.orgChild2Id) || []).forEach((x3) => {
          const childLevel4 = buildChildren(5, x3, {
            shortName: x3.orgChild3.orgChild3ShortName,
            name: x3.orgChild3.orgChild3Name,
            id: x3.orgChild3.id,
          });
          const node = createNode(x3, 4, {
            shortName: x3.orgChild3.orgChild3ShortName,
            name: x3.orgChild3.orgChild3Name,
            id: x3.orgChild3.id,
          });
          node.children = childLevel4;
          children.push(node);
        });
        return children;
      } else if (level === 5) {
        // Level 5: child3 non-directors and child4 directors
        const children: any[] = [];
        (child3ByOrgChild3Id.get(parent.orgChild3Id) || []).forEach((x33) => {
          children.push(
            createNode(x33, 5, {
              shortName: x33.orgChild3.orgChild3ShortName,
              name: x33.orgChild3.orgChild3Name,
              id: x33.orgChild3.id,
            }),
          );
        });
        (child4ByOrgChild3Id.get(parent.orgChild3Id) || []).forEach((x4) => {
          // Level 5: child4 directors and their non-directors
          const childLevel5: any[] = [];
          posMasterChild4
            .filter((x) => !x.isDirector && (x as any)[fieldId] && x.orgChild3Id === x4.orgChild3Id)
            .forEach((x44) => {
              childLevel5.push(
                createNode(x44, 5, {
                  shortName: x44.orgChild4.orgChild4ShortName,
                  name: x44.orgChild4.orgChild4Name,
                  id: x44.orgChild4.id,
                }),
              );
            });
          const node = createNode(x4, 4, {
            shortName: x4.orgChild4.orgChild4ShortName,
            name: x4.orgChild4.orgChild4Name,
            id: x4.orgChild4.id,
          });
          node.children = childLevel5;
          children.push(node);
        });
        return children;
      }
      return [];
    }

    // Build root nodes
    const formattedData = posMasterRoot
      .filter(
        (x: any) =>
          x.isDirector && minPosMasterNoByOrgRootId.get(x.orgRootId ?? "") === x.posMasterNo,
      )
      .map((x0) => {
        const rootNode = createNode(x0, 1, {
          shortName: x0.orgRoot.orgRootShortName,
          name: x0.orgRoot.orgRootName,
          id: x0.orgRoot.id,
        });
        rootNode.children = buildChildren(2, x0, {
          shortName: x0.orgRoot.orgRootShortName,
          name: x0.orgRoot.orgRootName,
          id: x0.orgRoot.id,
        });
        return rootNode;
      });

    const formattedData_ =
      rootId === "root"
        ? [
            {
              personID: "",
              name: "",
              avatar: "",
              positionName: "",
              positionNum: "",
              positionNumInt: null,
              departmentName: data.orgRevisionName,
              organizationId: data.id,
              children: formattedData,
            },
          ]
        : formattedData;
    return new HttpSuccess(formattedData_);
  }

  // /**
  //  * API Organizational StructChart
  //  *
  //  * @summary Organizational StructChart
  //  *
  //  */
  // @Get("struct-chart/{idNode}/{type}")
  // async structchart(@Path() idNode: string, type: number) {
  //   switch (type) {
  //     case 0: {
  //       const data = await this.orgRevisionRepository.findOne({
  //         where: { id: idNode },
  //         relations: [
  //           "orgRoots",
  //           "orgRoots.orgChild1s",
  //           "orgRoots.orgChild1s.orgChild2s",
  //           "orgRoots.orgChild1s.orgChild2s.orgChild3s",
  //           "orgRoots.orgChild1s.orgChild2s.orgChild3s.orgChild4s",
  //         ],
  //       });
  //       if (!data) {
  //         throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revision");
  //       }

  //       const formattedData = {
  //         departmentName: data.orgRevisionName,
  //         deptID: data.id,
  //         type: 0,
  //         // heads:
  //         totalPositionCount: await this.posMasterRepository.count({
  //           where: { orgRevisionId: data.id },
  //         }),
  //         totalPositionVacant:
  //           data.orgRevisionIsDraft == true
  //             ? await this.posMasterRepository.count({
  //                 where: {
  //                   orgRevisionId: data.id,
  //                   next_holderId: IsNull() || "",
  //                 },
  //               })
  //             : await this.posMasterRepository.count({
  //                 where: {
  //                   orgRevisionId: data.id,
  //                   current_holderId: IsNull() || "",
  //                 },
  //               }),
  //         // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgRevisionId: data.id,
  //         //     current_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //         // totalPositionNextVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgRevisionId: data.id,
  //         //     next_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //         children: await Promise.all(
  //           data.orgRoots
  //             .sort((a, b) => a.orgRootOrder - b.orgRootOrder)
  //             .map(async (orgRoot: OrgRoot) => {
  //               return {
  //                 departmentName: orgRoot.orgRootName,
  //                 deptID: orgRoot.id,
  //                 type: 1,
  //                 // heads:
  //                 totalPositionCount: await this.posMasterRepository.count({
  //                   where: { orgRevisionId: data.id, orgRootId: orgRoot.id },
  //                 }),
  //                 totalPositionVacant:
  //                   data.orgRevisionIsDraft == true
  //                     ? await this.posMasterRepository.count({
  //                         where: {
  //                           orgRevisionId: data.id,
  //                           orgRootId: orgRoot.id,
  //                           next_holderId: IsNull() || "",
  //                         },
  //                       })
  //                     : await this.posMasterRepository.count({
  //                         where: {
  //                           orgRevisionId: data.id,
  //                           orgRootId: orgRoot.id,
  //                           current_holderId: IsNull() || "",
  //                         },
  //                       }),
  //                 // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                 //   where: {
  //                 //     orgRevisionId: data.id,
  //                 //     orgRootId: orgRoot.id,
  //                 //     current_holderId: IsNull() || "",
  //                 //   },
  //                 // }),
  //                 // totalPositionNextVacant: await this.posMasterRepository.count({
  //                 //   where: {
  //                 //     orgRevisionId: data.id,
  //                 //     orgRootId: orgRoot.id,
  //                 //     next_holderId: IsNull() || "",
  //                 //   },
  //                 // }),
  //                 children: await Promise.all(
  //                   orgRoot.orgChild1s
  //                     .sort((a, b) => a.orgChild1Order - b.orgChild1Order)
  //                     .map(async (orgChild1) => ({
  //                       departmentName: orgChild1.orgChild1Name,
  //                       deptID: orgChild1.id,
  //                       type: 2,
  //                       // heads:
  //                       totalPositionCount: await this.posMasterRepository.count({
  //                         where: {
  //                           orgRevisionId: data.id,
  //                           orgRootId: orgRoot.id,
  //                           orgChild1Id: orgChild1.id,
  //                         },
  //                       }),
  //                       totalPositionVacant:
  //                         data.orgRevisionIsDraft == true
  //                           ? await this.posMasterRepository.count({
  //                               where: {
  //                                 orgRevisionId: data.id,
  //                                 orgRootId: orgRoot.id,
  //                                 orgChild1Id: orgChild1.id,
  //                                 next_holderId: IsNull() || "",
  //                               },
  //                             })
  //                           : await this.posMasterRepository.count({
  //                               where: {
  //                                 orgRevisionId: data.id,
  //                                 orgRootId: orgRoot.id,
  //                                 orgChild1Id: orgChild1.id,
  //                                 current_holderId: IsNull() || "",
  //                               },
  //                             }),
  //                       //   totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                       //     where: {
  //                       //       orgRevisionId: data.id,
  //                       //       orgRootId: orgRoot.id,
  //                       //       orgChild1Id: orgChild1.id,
  //                       //       current_holderId: IsNull() || "",
  //                       //     },
  //                       //   }),
  //                       //   totalPositionNextVacant: await this.posMasterRepository.count({
  //                       //     where: {
  //                       //       orgRevisionId: data.id,
  //                       //       orgRootId: orgRoot.id,
  //                       //       orgChild1Id: orgChild1.id,
  //                       //       next_holderId: IsNull() || "",
  //                       //     },
  //                       //   }),
  //                       children: await Promise.all(
  //                         orgChild1.orgChild2s
  //                           .sort((a, b) => a.orgChild2Order - b.orgChild2Order)
  //                           .map(async (orgChild2) => ({
  //                             departmentName: orgChild2.orgChild2Name,
  //                             deptID: orgChild2.id,
  //                             type: 3,
  //                             // heads:
  //                             totalPositionCount: await this.posMasterRepository.count({
  //                               where: {
  //                                 orgRevisionId: data.id,
  //                                 orgRootId: orgRoot.id,
  //                                 orgChild1Id: orgChild1.id,
  //                                 orgChild2Id: orgChild2.id,
  //                               },
  //                             }),
  //                             totalPositionVacant:
  //                               data.orgRevisionIsDraft == true
  //                                 ? await this.posMasterRepository.count({
  //                                     where: {
  //                                       orgRevisionId: data.id,
  //                                       orgRootId: orgRoot.id,
  //                                       orgChild1Id: orgChild1.id,
  //                                       orgChild2Id: orgChild2.id,
  //                                       next_holderId: IsNull() || "",
  //                                     },
  //                                   })
  //                                 : await this.posMasterRepository.count({
  //                                     where: {
  //                                       orgRevisionId: data.id,
  //                                       orgRootId: orgRoot.id,
  //                                       orgChild1Id: orgChild1.id,
  //                                       orgChild2Id: orgChild2.id,
  //                                       current_holderId: IsNull() || "",
  //                                     },
  //                                   }),
  //                             // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                             //   where: {
  //                             //     orgRevisionId: data.id,
  //                             //     orgRootId: orgRoot.id,
  //                             //     orgChild1Id: orgChild1.id,
  //                             //     orgChild2Id: orgChild2.id,
  //                             //     current_holderId: IsNull() || "",
  //                             //   },
  //                             // }),
  //                             // totalPositionNextVacant: await this.posMasterRepository.count({
  //                             //   where: {
  //                             //     orgRevisionId: data.id,
  //                             //     orgRootId: orgRoot.id,
  //                             //     orgChild1Id: orgChild1.id,
  //                             //     orgChild2Id: orgChild2.id,
  //                             //     next_holderId: IsNull() || "",
  //                             //   },
  //                             // }),
  //                             children: await Promise.all(
  //                               orgChild2.orgChild3s
  //                                 .sort((a, b) => a.orgChild3Order - b.orgChild3Order)
  //                                 .map(async (orgChild3) => ({
  //                                   departmentName: orgChild3.orgChild3Name,
  //                                   deptID: orgChild3.id,
  //                                   type: 4,
  //                                   // heads:
  //                                   totalPositionCount: await this.posMasterRepository.count({
  //                                     where: {
  //                                       orgRevisionId: data.id,
  //                                       orgRootId: orgRoot.id,
  //                                       orgChild1Id: orgChild1.id,
  //                                       orgChild2Id: orgChild2.id,
  //                                       orgChild3Id: orgChild3.id,
  //                                     },
  //                                   }),
  //                                   totalPositionVacant:
  //                                     data.orgRevisionIsDraft == true
  //                                       ? await this.posMasterRepository.count({
  //                                           where: {
  //                                             orgRevisionId: data.id,
  //                                             orgRootId: orgRoot.id,
  //                                             orgChild1Id: orgChild1.id,
  //                                             orgChild2Id: orgChild2.id,
  //                                             orgChild3Id: orgChild3.id,
  //                                             next_holderId: IsNull() || "",
  //                                           },
  //                                         })
  //                                       : await this.posMasterRepository.count({
  //                                           where: {
  //                                             orgRevisionId: data.id,
  //                                             orgRootId: orgRoot.id,
  //                                             orgChild1Id: orgChild1.id,
  //                                             orgChild2Id: orgChild2.id,
  //                                             orgChild3Id: orgChild3.id,
  //                                             current_holderId: IsNull() || "",
  //                                           },
  //                                         }),
  //                                   //   totalPositionCurrentVacant:
  //                                   //     await this.posMasterRepository.count({
  //                                   //       where: {
  //                                   //         orgRevisionId: data.id,
  //                                   //         orgRootId: orgRoot.id,
  //                                   //         orgChild1Id: orgChild1.id,
  //                                   //         orgChild2Id: orgChild2.id,
  //                                   //         orgChild3Id: orgChild3.id,
  //                                   //         current_holderId: IsNull() || "",
  //                                   //       },
  //                                   //     }),
  //                                   //   totalPositionNextVacant: await this.posMasterRepository.count(
  //                                   //     {
  //                                   //       where: {
  //                                   //         orgRevisionId: data.id,
  //                                   //         orgRootId: orgRoot.id,
  //                                   //         orgChild1Id: orgChild1.id,
  //                                   //         orgChild2Id: orgChild2.id,
  //                                   //         orgChild3Id: orgChild3.id,
  //                                   //         next_holderId: IsNull() || "",
  //                                   //       },
  //                                   //     },
  //                                   //   ),
  //                                   children: await Promise.all(
  //                                     orgChild3.orgChild4s
  //                                       .sort((a, b) => a.orgChild4Order - b.orgChild4Order)
  //                                       .map(async (orgChild4) => ({
  //                                         departmentName: orgChild4.orgChild4Name,
  //                                         deptID: orgChild4.id,
  //                                         type: 5,
  //                                         // heads:
  //                                         totalPositionCount: await this.posMasterRepository.count({
  //                                           where: {
  //                                             orgRevisionId: data.id,
  //                                             orgRootId: orgRoot.id,
  //                                             orgChild1Id: orgChild1.id,
  //                                             orgChild2Id: orgChild2.id,
  //                                             orgChild3Id: orgChild3.id,
  //                                             orgChild4Id: orgChild4.id,
  //                                           },
  //                                         }),
  //                                         totalPositionVacant:
  //                                           data.orgRevisionIsDraft == true
  //                                             ? await this.posMasterRepository.count({
  //                                                 where: {
  //                                                   orgRevisionId: data.id,
  //                                                   orgRootId: orgRoot.id,
  //                                                   orgChild1Id: orgChild1.id,
  //                                                   orgChild2Id: orgChild2.id,
  //                                                   orgChild3Id: orgChild3.id,
  //                                                   orgChild4Id: orgChild4.id,
  //                                                   next_holderId: IsNull() || "",
  //                                                 },
  //                                               })
  //                                             : await this.posMasterRepository.count({
  //                                                 where: {
  //                                                   orgRevisionId: data.id,
  //                                                   orgRootId: orgRoot.id,
  //                                                   orgChild1Id: orgChild1.id,
  //                                                   orgChild2Id: orgChild2.id,
  //                                                   orgChild3Id: orgChild3.id,
  //                                                   orgChild4Id: orgChild4.id,
  //                                                   current_holderId: IsNull() || "",
  //                                                 },
  //                                               }),
  //                                         // totalPositionCurrentVacant:
  //                                         //   await this.posMasterRepository.count({
  //                                         //     where: {
  //                                         //       orgRevisionId: data.id,
  //                                         //       orgRootId: orgRoot.id,
  //                                         //       orgChild1Id: orgChild1.id,
  //                                         //       orgChild2Id: orgChild2.id,
  //                                         //       orgChild3Id: orgChild3.id,
  //                                         //       orgChild4Id: orgChild4.id,
  //                                         //       current_holderId: IsNull() || "",
  //                                         //     },
  //                                         //   }),
  //                                         // totalPositionNextVacant:
  //                                         //   await this.posMasterRepository.count({
  //                                         //     where: {
  //                                         //       orgRevisionId: data.id,
  //                                         //       orgRootId: orgRoot.id,
  //                                         //       orgChild1Id: orgChild1.id,
  //                                         //       orgChild2Id: orgChild2.id,
  //                                         //       orgChild3Id: orgChild3.id,
  //                                         //       orgChild4Id: orgChild4.id,
  //                                         //       next_holderId: IsNull() || "",
  //                                         //     },
  //                                         //   }),
  //                                       })),
  //                                   ),
  //                                 })),
  //                             ),
  //                           })),
  //                       ),
  //                     })),
  //                 ),
  //               };
  //             }),
  //         ),
  //       };
  //       return new HttpSuccess([formattedData]);
  //     }
  //     case 1: {
  //       const data = await this.orgRootRepository.findOne({
  //         where: { id: idNode },
  //         relations: [
  //           "orgRevision",
  //           "orgChild1s",
  //           "orgChild1s.orgChild2s",
  //           "orgChild1s.orgChild2s.orgChild3s",
  //           "orgChild1s.orgChild2s.orgChild3s.orgChild4s",
  //         ],
  //       });
  //       if (!data) {
  //         throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId");
  //       }

  //       const formattedData = {
  //         departmentName: data.orgRootName,
  //         deptID: data.id,
  //         type: 1,
  //         // heads:
  //         totalPositionCount: await this.posMasterRepository.count({
  //           where: { orgRootId: data.id },
  //         }),
  //         totalPositionVacant:
  //           data.orgRevision.orgRevisionIsDraft == true
  //             ? await this.posMasterRepository.count({
  //                 where: {
  //                   orgRootId: data.id,
  //                   next_holderId: IsNull() || "",
  //                 },
  //               })
  //             : await this.posMasterRepository.count({
  //                 where: {
  //                   orgRootId: data.id,
  //                   current_holderId: IsNull() || "",
  //                 },
  //               }),
  //         // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgRootId: data.id,
  //         //     current_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //         // totalPositionNextVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgRootId: data.id,
  //         //     next_holderId: IsNull() || "",
  //         //   },
  //         // }),

  //         children: await Promise.all(
  //           data.orgChild1s
  //             .sort((a, b) => a.orgChild1Order - b.orgChild1Order)
  //             .map(async (orgChild1) => ({
  //               departmentName: orgChild1.orgChild1Name,
  //               deptID: orgChild1.id,
  //               type: 2,
  //               // heads:
  //               totalPositionCount: await this.posMasterRepository.count({
  //                 where: { orgRootId: data.id, orgChild1Id: orgChild1.id },
  //               }),
  //               totalPositionVacant:
  //                 data.orgRevision.orgRevisionIsDraft == true
  //                   ? await this.posMasterRepository.count({
  //                       where: {
  //                         orgRootId: data.id,
  //                         orgChild1Id: orgChild1.id,
  //                         next_holderId: IsNull() || "",
  //                       },
  //                     })
  //                   : await this.posMasterRepository.count({
  //                       where: {
  //                         orgRootId: data.id,
  //                         orgChild1Id: orgChild1.id,
  //                         current_holderId: IsNull() || "",
  //                       },
  //                     }),
  //               //   totalPositionCurrentVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgRootId: data.id,
  //               //       orgChild1Id: orgChild1.id,
  //               //       current_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //               //   totalPositionNextVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgRootId: data.id,
  //               //       orgChild1Id: orgChild1.id,
  //               //       next_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //               children: await Promise.all(
  //                 orgChild1.orgChild2s
  //                   .sort((a, b) => a.orgChild2Order - b.orgChild2Order)
  //                   .map(async (orgChild2) => ({
  //                     departmentName: orgChild2.orgChild2Name,
  //                     deptID: orgChild2.id,
  //                     type: 3,
  //                     // heads:
  //                     totalPositionCount: await this.posMasterRepository.count({
  //                       where: {
  //                         orgRootId: data.id,
  //                         orgChild1Id: orgChild1.id,
  //                         orgChild2Id: orgChild2.id,
  //                       },
  //                     }),
  //                     totalPositionCurrentVacant:
  //                       data.orgRevision.orgRevisionIsDraft == true
  //                         ? await this.posMasterRepository.count({
  //                             where: {
  //                               orgRootId: data.id,
  //                               orgChild1Id: orgChild1.id,
  //                               orgChild2Id: orgChild2.id,
  //                               next_holderId: IsNull() || "",
  //                             },
  //                           })
  //                         : await this.posMasterRepository.count({
  //                             where: {
  //                               orgRootId: data.id,
  //                               orgChild1Id: orgChild1.id,
  //                               orgChild2Id: orgChild2.id,
  //                               current_holderId: IsNull() || "",
  //                             },
  //                           }),
  //                     // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                     //   where: {
  //                     //     orgRootId: data.id,
  //                     //     orgChild1Id: orgChild1.id,
  //                     //     orgChild2Id: orgChild2.id,
  //                     //     current_holderId: IsNull() || "",
  //                     //   },
  //                     // }),
  //                     // totalPositionNextVacant: await this.posMasterRepository.count({
  //                     //   where: {
  //                     //     orgRootId: data.id,
  //                     //     orgChild1Id: orgChild1.id,
  //                     //     orgChild2Id: orgChild2.id,
  //                     //     next_holderId: IsNull() || "",
  //                     //   },
  //                     // }),
  //                     children: await Promise.all(
  //                       orgChild2.orgChild3s
  //                         .sort((a, b) => a.orgChild3Order - b.orgChild3Order)
  //                         .map(async (orgChild3) => ({
  //                           departmentName: orgChild3.orgChild3Name,
  //                           deptID: orgChild3.id,
  //                           type: 4,
  //                           // heads:
  //                           totalPositionCount: await this.posMasterRepository.count({
  //                             where: {
  //                               orgRootId: data.id,
  //                               orgChild1Id: orgChild1.id,
  //                               orgChild2Id: orgChild2.id,
  //                               orgChild3Id: orgChild3.id,
  //                             },
  //                           }),
  //                           totalPositionVacant:
  //                             data.orgRevision.orgRevisionIsDraft == true
  //                               ? await this.posMasterRepository.count({
  //                                   where: {
  //                                     orgRootId: data.id,
  //                                     orgChild1Id: orgChild1.id,
  //                                     orgChild2Id: orgChild2.id,
  //                                     orgChild3Id: orgChild3.id,
  //                                     next_holderId: IsNull() || "",
  //                                   },
  //                                 })
  //                               : await this.posMasterRepository.count({
  //                                   where: {
  //                                     orgRootId: data.id,
  //                                     orgChild1Id: orgChild1.id,
  //                                     orgChild2Id: orgChild2.id,
  //                                     orgChild3Id: orgChild3.id,
  //                                     current_holderId: IsNull() || "",
  //                                   },
  //                                 }),
  //                           //   totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                           //     where: {
  //                           //       orgRootId: data.id,
  //                           //       orgChild1Id: orgChild1.id,
  //                           //       orgChild2Id: orgChild2.id,
  //                           //       orgChild3Id: orgChild3.id,
  //                           //       current_holderId: IsNull() || "",
  //                           //     },
  //                           //   }),
  //                           //   totalPositionNextVacant: await this.posMasterRepository.count({
  //                           //     where: {
  //                           //       orgRootId: data.id,
  //                           //       orgChild1Id: orgChild1.id,
  //                           //       orgChild2Id: orgChild2.id,
  //                           //       orgChild3Id: orgChild3.id,
  //                           //       next_holderId: IsNull() || "",
  //                           //     },
  //                           //   }),
  //                           children: await Promise.all(
  //                             orgChild3.orgChild4s
  //                               .sort((a, b) => a.orgChild4Order - b.orgChild4Order)
  //                               .map(async (orgChild4) => ({
  //                                 departmentName: orgChild4.orgChild4Name,
  //                                 deptID: orgChild4.id,
  //                                 type: 5,
  //                                 // heads:
  //                                 totalPositionCount: await this.posMasterRepository.count({
  //                                   where: {
  //                                     orgRootId: data.id,
  //                                     orgChild1Id: orgChild1.id,
  //                                     orgChild2Id: orgChild2.id,
  //                                     orgChild3Id: orgChild3.id,
  //                                     orgChild4Id: orgChild4.id,
  //                                   },
  //                                 }),
  //                                 totalPositionVacant:
  //                                   data.orgRevision.orgRevisionIsDraft == true
  //                                     ? await this.posMasterRepository.count({
  //                                         where: {
  //                                           orgRootId: data.id,
  //                                           orgChild1Id: orgChild1.id,
  //                                           orgChild2Id: orgChild2.id,
  //                                           orgChild3Id: orgChild3.id,
  //                                           orgChild4Id: orgChild4.id,
  //                                           next_holderId: IsNull() || "",
  //                                         },
  //                                       })
  //                                     : await this.posMasterRepository.count({
  //                                         where: {
  //                                           orgRootId: data.id,
  //                                           orgChild1Id: orgChild1.id,
  //                                           orgChild2Id: orgChild2.id,
  //                                           orgChild3Id: orgChild3.id,
  //                                           orgChild4Id: orgChild4.id,
  //                                           current_holderId: IsNull() || "",
  //                                         },
  //                                       }),
  //                                 // totalPositionCurrentVacant:
  //                                 //   await this.posMasterRepository.count({
  //                                 //     where: {
  //                                 //       orgRootId: data.id,
  //                                 //       orgChild1Id: orgChild1.id,
  //                                 //       orgChild2Id: orgChild2.id,
  //                                 //       orgChild3Id: orgChild3.id,
  //                                 //       orgChild4Id: orgChild4.id,
  //                                 //       current_holderId: IsNull() || "",
  //                                 //     },
  //                                 //   }),
  //                                 // totalPositionNextVacant: await this.posMasterRepository.count({
  //                                 //   where: {
  //                                 //     orgRootId: data.id,
  //                                 //     orgChild1Id: orgChild1.id,
  //                                 //     orgChild2Id: orgChild2.id,
  //                                 //     orgChild3Id: orgChild3.id,
  //                                 //     orgChild4Id: orgChild4.id,
  //                                 //     next_holderId: IsNull() || "",
  //                                 //   },
  //                                 // }),
  //                               })),
  //                           ),
  //                         })),
  //                     ),
  //                   })),
  //               ),
  //             })),
  //         ),
  //       };
  //       return new HttpSuccess([formattedData]);
  //     }
  //     case 2: {
  //       const data = await this.child1Repository.findOne({
  //         where: { id: idNode },
  //         relations: [
  //           "orgRevision",
  //           "orgChild2s",
  //           "orgChild2s.orgChild3s",
  //           "orgChild2s.orgChild3s.orgChild4s",
  //         ],
  //       });
  //       if (!data) {
  //         throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id");
  //       }

  //       const formattedData = {
  //         departmentName: data.orgChild1Name,
  //         deptID: data.id,
  //         type: 2,
  //         // heads:
  //         totalPositionCount: await this.posMasterRepository.count({
  //           where: { orgChild1Id: data.id },
  //         }),
  //         totalPositionVacant:
  //           data.orgRevision.orgRevisionIsDraft == true
  //             ? await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild1Id: data.id,
  //                   next_holderId: IsNull() || "",
  //                 },
  //               })
  //             : await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild1Id: data.id,
  //                   current_holderId: IsNull() || "",
  //                 },
  //               }),
  //         // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild1Id: data.id,
  //         //     current_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //         // totalPositionNextVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild1Id: data.id,
  //         //     next_holderId: IsNull() || "",
  //         //   },
  //         // }),

  //         children: await Promise.all(
  //           data.orgChild2s
  //             .sort((a, b) => a.orgChild2Order - b.orgChild2Order)
  //             .map(async (orgChild2) => ({
  //               departmentName: orgChild2.orgChild2Name,
  //               deptID: orgChild2.id,
  //               type: 3,
  //               // heads:
  //               totalPositionCount: await this.posMasterRepository.count({
  //                 where: { orgChild1Id: data.id, orgChild2Id: orgChild2.id },
  //               }),
  //               totalPositionVacant:
  //                 data.orgRevision.orgRevisionIsDraft == true
  //                   ? await this.posMasterRepository.count({
  //                       where: {
  //                         orgChild1Id: data.id,
  //                         orgChild2Id: orgChild2.id,
  //                         next_holderId: IsNull() || "",
  //                       },
  //                     })
  //                   : await this.posMasterRepository.count({
  //                       where: {
  //                         orgChild1Id: data.id,
  //                         orgChild2Id: orgChild2.id,
  //                         current_holderId: IsNull() || "",
  //                       },
  //                     }),
  //               //   totalPositionCurrentVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgChild1Id: data.id,
  //               //       orgChild2Id: orgChild2.id,
  //               //       current_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //               //   totalPositionNextVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgChild1Id: data.id,
  //               //       orgChild2Id: orgChild2.id,
  //               //       next_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //               children: await Promise.all(
  //                 orgChild2.orgChild3s
  //                   .sort((a, b) => a.orgChild3Order - b.orgChild3Order)
  //                   .map(async (orgChild3) => ({
  //                     departmentName: orgChild3.orgChild3Name,
  //                     deptID: orgChild3.id,
  //                     type: 4,
  //                     // heads:
  //                     totalPositionCount: await this.posMasterRepository.count({
  //                       where: {
  //                         orgRevisionId: data.id,
  //                         orgChild2Id: orgChild2.id,
  //                         orgChild3Id: orgChild3.id,
  //                       },
  //                     }),
  //                     totalPositionVacant:
  //                       data.orgRevision.orgRevisionIsDraft == true
  //                         ? await this.posMasterRepository.count({
  //                             where: {
  //                               orgRevisionId: data.id,
  //                               orgChild2Id: orgChild2.id,
  //                               orgChild3Id: orgChild3.id,
  //                               next_holderId: IsNull() || "",
  //                             },
  //                           })
  //                         : await this.posMasterRepository.count({
  //                             where: {
  //                               orgRevisionId: data.id,
  //                               orgChild2Id: orgChild2.id,
  //                               orgChild3Id: orgChild3.id,
  //                               current_holderId: IsNull() || "",
  //                             },
  //                           }),
  //                     // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                     //   where: {
  //                     //     orgRevisionId: data.id,
  //                     //     orgChild2Id: orgChild2.id,
  //                     //     orgChild3Id: orgChild3.id,
  //                     //     current_holderId: IsNull() || "",
  //                     //   },
  //                     // }),
  //                     // totalPositionNextVacant: await this.posMasterRepository.count({
  //                     //   where: {
  //                     //     orgRevisionId: data.id,
  //                     //     orgChild2Id: orgChild2.id,
  //                     //     orgChild3Id: orgChild3.id,
  //                     //     next_holderId: IsNull() || "",
  //                     //   },
  //                     // }),
  //                     children: await Promise.all(
  //                       orgChild3.orgChild4s
  //                         .sort((a, b) => a.orgChild4Order - b.orgChild4Order)
  //                         .map(async (orgChild4) => ({
  //                           departmentName: orgChild4.orgChild4Name,
  //                           deptID: orgChild4.id,
  //                           type: 5,
  //                           // heads:
  //                           totalPositionCount: await this.posMasterRepository.count({
  //                             where: {
  //                               orgRevisionId: data.id,
  //                               orgChild2Id: orgChild2.id,
  //                               orgChild3Id: orgChild3.id,
  //                               orgChild4Id: orgChild4.id,
  //                             },
  //                           }),
  //                           totalPositionVacant:
  //                             data.orgRevision.orgRevisionIsDraft == true
  //                               ? await this.posMasterRepository.count({
  //                                   where: {
  //                                     orgRevisionId: data.id,
  //                                     orgChild2Id: orgChild2.id,
  //                                     orgChild3Id: orgChild3.id,
  //                                     orgChild4Id: orgChild4.id,
  //                                     next_holderId: IsNull() || "",
  //                                   },
  //                                 })
  //                               : await this.posMasterRepository.count({
  //                                   where: {
  //                                     orgRevisionId: data.id,
  //                                     orgChild2Id: orgChild2.id,
  //                                     orgChild3Id: orgChild3.id,
  //                                     orgChild4Id: orgChild4.id,
  //                                     current_holderId: IsNull() || "",
  //                                   },
  //                                 }),
  //                           //   totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                           //     where: {
  //                           //       orgRevisionId: data.id,
  //                           //       orgChild2Id: orgChild2.id,
  //                           //       orgChild3Id: orgChild3.id,
  //                           //       orgChild4Id: orgChild4.id,
  //                           //       current_holderId: IsNull() || "",
  //                           //     },
  //                           //   }),
  //                           //   totalPositionNextVacant: await this.posMasterRepository.count({
  //                           //     where: {
  //                           //       orgRevisionId: data.id,
  //                           //       orgChild2Id: orgChild2.id,
  //                           //       orgChild3Id: orgChild3.id,
  //                           //       orgChild4Id: orgChild4.id,
  //                           //       next_holderId: IsNull() || "",
  //                           //     },
  //                           //   }),
  //                         })),
  //                     ),
  //                   })),
  //               ),
  //             })),
  //         ),
  //       };
  //       return new HttpSuccess([formattedData]);
  //     }
  //     case 3: {
  //       const data = await this.child2Repository.findOne({
  //         where: { id: idNode },
  //         relations: ["orgRevision", "orgChild3s", "orgChild3s.orgChild4s"],
  //       });
  //       if (!data) {
  //         throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id");
  //       }

  //       const formattedData = {
  //         departmentName: data.orgChild2Name,
  //         deptID: data.id,
  //         type: 3,
  //         // heads:
  //         totalPositionCount: await this.posMasterRepository.count({
  //           where: { orgChild2Id: data.id },
  //         }),
  //         totalPositionVacant:
  //           data.orgRevision.orgRevisionIsDraft == true
  //             ? await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild2Id: data.id,
  //                   next_holderId: IsNull() || "",
  //                 },
  //               })
  //             : await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild2Id: data.id,
  //                   current_holderId: IsNull() || "",
  //                 },
  //               }),
  //         // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild2Id: data.id,
  //         //     current_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //         // totalPositionNextVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild2Id: data.id,
  //         //     next_holderId: IsNull() || "",
  //         //   },
  //         // }),

  //         children: await Promise.all(
  //           data.orgChild3s
  //             .sort((a, b) => a.orgChild3Order - b.orgChild3Order)
  //             .map(async (orgChild3) => ({
  //               departmentName: orgChild3.orgChild3Name,
  //               deptID: orgChild3.id,
  //               type: 4,
  //               // heads:
  //               totalPositionCount: await this.posMasterRepository.count({
  //                 where: { orgChild2Id: data.id, orgChild3Id: orgChild3.id },
  //               }),
  //               totalPositionVacant:
  //                 data.orgRevision.orgRevisionIsDraft == true
  //                   ? await this.posMasterRepository.count({
  //                       where: {
  //                         orgChild2Id: data.id,
  //                         orgChild3Id: orgChild3.id,
  //                         next_holderId: IsNull() || "",
  //                       },
  //                     })
  //                   : await this.posMasterRepository.count({
  //                       where: {
  //                         orgChild2Id: data.id,
  //                         orgChild3Id: orgChild3.id,
  //                         current_holderId: IsNull() || "",
  //                       },
  //                     }),
  //               //   totalPositionCurrentVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgChild2Id: data.id,
  //               //       orgChild3Id: orgChild3.id,
  //               //       current_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //               //   totalPositionNextVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgChild2Id: data.id,
  //               //       orgChild3Id: orgChild3.id,
  //               //       next_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //               children: await Promise.all(
  //                 orgChild3.orgChild4s
  //                   .sort((a, b) => a.orgChild4Order - b.orgChild4Order)
  //                   .map(async (orgChild4) => ({
  //                     departmentName: orgChild4.orgChild4Name,
  //                     deptID: orgChild4.id,
  //                     type: 5,
  //                     // heads:
  //                     totalPositionCount: await this.posMasterRepository.count({
  //                       where: {
  //                         orgChild2Id: data.id,
  //                         orgChild3Id: orgChild3.id,
  //                         orgChild4Id: orgChild4.id,
  //                       },
  //                     }),
  //                     totalPositionVacant:
  //                       data.orgRevision.orgRevisionIsDraft == true
  //                         ? await this.posMasterRepository.count({
  //                             where: {
  //                               orgChild2Id: data.id,
  //                               orgChild3Id: orgChild3.id,
  //                               orgChild4Id: orgChild4.id,
  //                               next_holderId: IsNull() || "",
  //                             },
  //                           })
  //                         : await this.posMasterRepository.count({
  //                             where: {
  //                               orgChild2Id: data.id,
  //                               orgChild3Id: orgChild3.id,
  //                               orgChild4Id: orgChild4.id,
  //                               current_holderId: IsNull() || "",
  //                             },
  //                           }),
  //                     // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //                     //   where: {
  //                     //     orgChild2Id: data.id,
  //                     //     orgChild3Id: orgChild3.id,
  //                     //     orgChild4Id: orgChild4.id,
  //                     //     current_holderId: IsNull() || "",
  //                     //   },
  //                     // }),
  //                     // totalPositionNextVacant: await this.posMasterRepository.count({
  //                     //   where: {
  //                     //     orgChild2Id: data.id,
  //                     //     orgChild3Id: orgChild3.id,
  //                     //     orgChild4Id: orgChild4.id,
  //                     //     next_holderId: IsNull() || "",
  //                     //   },
  //                     // }),
  //                   })),
  //               ),
  //             })),
  //         ),
  //       };
  //       return new HttpSuccess([formattedData]);
  //     }
  //     case 4: {
  //       const data = await this.child3Repository.findOne({
  //         where: { id: idNode },
  //         relations: ["orgRevision", "orgChild4s"],
  //       });
  //       if (!data) {
  //         throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id");
  //       }

  //       const formattedData = {
  //         departmentName: data.orgChild3Name,
  //         deptID: data.id,
  //         type: 4,
  //         // heads:
  //         totalPositionCount: await this.posMasterRepository.count({
  //           where: { orgChild3Id: data.id },
  //         }),
  //         totalPositionVacant:
  //           data.orgRevision.orgRevisionIsDraft == true
  //             ? await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild3Id: data.id,
  //                   next_holderId: IsNull() || "",
  //                 },
  //               })
  //             : await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild3Id: data.id,
  //                   current_holderId: IsNull() || "",
  //                 },
  //               }),
  //         // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild3Id: data.id,
  //         //     current_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //         // totalPositionNextVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild3Id: data.id,
  //         //     next_holderId: IsNull() || "",
  //         //   },
  //         // }),

  //         children: await Promise.all(
  //           data.orgChild4s
  //             .sort((a, b) => a.orgChild4Order - b.orgChild4Order)
  //             .map(async (orgChild4) => ({
  //               departmentName: orgChild4.orgChild4Name,
  //               deptID: orgChild4.id,
  //               type: 5,
  //               // heads:
  //               totalPositionCount: await this.posMasterRepository.count({
  //                 where: { orgChild3Id: data.id, orgChild4Id: orgChild4.id },
  //               }),
  //               totalPositionVacant:
  //                 data.orgRevision.orgRevisionIsDraft == true
  //                   ? await this.posMasterRepository.count({
  //                       where: {
  //                         orgChild3Id: data.id,
  //                         orgChild4Id: orgChild4.id,
  //                         next_holderId: IsNull() || "",
  //                       },
  //                     })
  //                   : await this.posMasterRepository.count({
  //                       where: {
  //                         orgChild3Id: data.id,
  //                         orgChild4Id: orgChild4.id,
  //                         current_holderId: IsNull() || "",
  //                       },
  //                     }),
  //               //   totalPositionCurrentVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgChild3Id: data.id,
  //               //       orgChild4Id: orgChild4.id,
  //               //       current_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //               //   totalPositionNextVacant: await this.posMasterRepository.count({
  //               //     where: {
  //               //       orgChild3Id: data.id,
  //               //       orgChild4Id: orgChild4.id,
  //               //       next_holderId: IsNull() || "",
  //               //     },
  //               //   }),
  //             })),
  //         ),
  //       };
  //       return new HttpSuccess([formattedData]);
  //     }
  //     case 5: {
  //       const data = await this.child4Repository.findOne({
  //         where: { id: idNode },
  //         relations: ["orgRevision"],
  //       });
  //       if (!data) {
  //         throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4Id");
  //       }

  //       const formattedData = {
  //         departmentName: data.orgChild4Name,
  //         deptID: data.id,
  //         type: 5,
  //         // heads:
  //         totalPositionCount: await this.posMasterRepository.count({
  //           where: { orgChild4Id: data.id },
  //         }),
  //         totalPositionVacant:
  //           data.orgRevision.orgRevisionIsDraft == true
  //             ? await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild4Id: data.id,
  //                   next_holderId: IsNull() || "",
  //                 },
  //               })
  //             : await this.posMasterRepository.count({
  //                 where: {
  //                   orgChild4Id: data.id,
  //                   current_holderId: IsNull() || "",
  //                 },
  //               }),
  //         // totalPositionCurrentVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild4Id: data.id,
  //         //     current_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //         // totalPositionNextVacant: await this.posMasterRepository.count({
  //         //   where: {
  //         //     orgChild4Id: data.id,
  //         //     next_holderId: IsNull() || "",
  //         //   },
  //         // }),
  //       };
  //       return new HttpSuccess([formattedData]);
  //     }
  //     default:
  //       throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: ");
  //   }
  // }

  /**
   * API Organizational StructChart V2 (Optimized)
   *
   * @summary Organizational StructChart - Optimized with batch queries to prevent N+1 problem
   *
   */
  @Get("struct-chart/{idNode}/{type}")
  async structchartV2(@Path() idNode: string, type: number) {
    // Fetch orgRevisionId and isDraft status first
    let orgRevisionId: string | null = null;
    let isDraft = false;

    switch (type) {
      case 0:
        const revision = await this.orgRevisionRepository.findOne({ where: { id: idNode } });
        orgRevisionId = revision?.id || null;
        isDraft = revision?.orgRevisionIsDraft || false;
        break;
      case 1: {
        const root = await this.orgRootRepository.findOne({
          where: { id: idNode },
          relations: ["orgRevision"],
        });
        orgRevisionId = root?.orgRevision?.id || null;
        isDraft = root?.orgRevision?.orgRevisionIsDraft || false;
        break;
      }
      case 2: {
        const child1 = await this.child1Repository.findOne({
          where: { id: idNode },
          relations: ["orgRevision"],
        });
        orgRevisionId = child1?.orgRevision?.id || null;
        isDraft = child1?.orgRevision?.orgRevisionIsDraft || false;
        break;
      }
      case 3: {
        const child2 = await this.child2Repository.findOne({
          where: { id: idNode },
          relations: ["orgRevision"],
        });
        orgRevisionId = child2?.orgRevision?.id || null;
        isDraft = child2?.orgRevision?.orgRevisionIsDraft || false;
        break;
      }
      case 4: {
        const child3 = await this.child3Repository.findOne({
          where: { id: idNode },
          relations: ["orgRevision"],
        });
        orgRevisionId = child3?.orgRevision?.id || null;
        isDraft = child3?.orgRevision?.orgRevisionIsDraft || false;
        break;
      }
      case 5: {
        const child4 = await this.child4Repository.findOne({
          where: { id: idNode },
          relations: ["orgRevision"],
        });
        orgRevisionId = child4?.orgRevision?.id || null;
        isDraft = child4?.orgRevision?.orgRevisionIsDraft || false;
        break;
      }
      default:
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: ");
    }

    if (!orgRevisionId) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revision");
    }

    // Fetch all position counts in a single batch query (optimized)
    const positionCounts = await getPositionCountsAggregated(orgRevisionId);

    switch (type) {
      case 0: {
        const data = await this.orgRevisionRepository.findOne({
          where: { id: idNode },
          relations: [
            "orgRoots",
            "orgRoots.orgChild1s",
            "orgRoots.orgChild1s.orgChild2s",
            "orgRoots.orgChild1s.orgChild2s.orgChild3s",
            "orgRoots.orgChild1s.orgChild2s.orgChild3s.orgChild4s",
          ],
        });
        if (!data) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revision");
        }

        const formattedData = {
          departmentName: data.orgRevisionName,
          deptID: data.id,
          type: 0,
          totalPositionCount: this.sumAllCounts(positionCounts.orgRootMap),
          totalPositionVacant: this.sumAllVacantCounts(positionCounts.orgRootMap, isDraft),
          children: this.buildOrgRoots(data.orgRoots, positionCounts, isDraft),
        };
        return new HttpSuccess([formattedData]);
      }
      case 1: {
        const data = await this.orgRootRepository.findOne({
          where: { id: idNode },
          relations: [
            "orgRevision",
            "orgChild1s",
            "orgChild1s.orgChild2s",
            "orgChild1s.orgChild2s.orgChild3s",
            "orgChild1s.orgChild2s.orgChild3s.orgChild4s",
          ],
        });
        if (!data) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId");
        }

        const rootCounts = getPositionCount(positionCounts.orgRootMap, data.id);
        const formattedData = {
          departmentName: data.orgRootName,
          deptID: data.id,
          type: 1,
          totalPositionCount: rootCounts.totalCount,
          totalPositionVacant: isDraft ? rootCounts.nextVacantCount : rootCounts.currentVacantCount,
          children: this.buildOrgChild1s(data.orgChild1s, positionCounts, isDraft),
        };
        return new HttpSuccess([formattedData]);
      }
      case 2: {
        const data = await this.child1Repository.findOne({
          where: { id: idNode },
          relations: [
            "orgRevision",
            "orgChild2s",
            "orgChild2s.orgChild3s",
            "orgChild2s.orgChild3s.orgChild4s",
          ],
        });
        if (!data) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id");
        }

        const child1Counts = getPositionCount(positionCounts.orgChild1Map, data.id);
        const formattedData = {
          departmentName: data.orgChild1Name,
          deptID: data.id,
          type: 2,
          totalPositionCount: child1Counts.totalCount,
          totalPositionVacant: isDraft
            ? child1Counts.nextVacantCount
            : child1Counts.currentVacantCount,
          children: this.buildOrgChild2s(data.orgChild2s, positionCounts, isDraft),
        };
        return new HttpSuccess([formattedData]);
      }
      case 3: {
        const data = await this.child2Repository.findOne({
          where: { id: idNode },
          relations: ["orgRevision", "orgChild3s", "orgChild3s.orgChild4s"],
        });
        if (!data) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id");
        }

        const child2Counts = getPositionCount(positionCounts.orgChild2Map, data.id);
        const formattedData = {
          departmentName: data.orgChild2Name,
          deptID: data.id,
          type: 3,
          totalPositionCount: child2Counts.totalCount,
          totalPositionVacant: isDraft
            ? child2Counts.nextVacantCount
            : child2Counts.currentVacantCount,
          children: this.buildOrgChild3s(data.orgChild3s, positionCounts, isDraft),
        };
        return new HttpSuccess([formattedData]);
      }
      case 4: {
        const data = await this.child3Repository.findOne({
          where: { id: idNode },
          relations: ["orgRevision", "orgChild4s"],
        });
        if (!data) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id");
        }

        const child3Counts = getPositionCount(positionCounts.orgChild3Map, data.id);
        const formattedData = {
          departmentName: data.orgChild3Name,
          deptID: data.id,
          type: 4,
          totalPositionCount: child3Counts.totalCount,
          totalPositionVacant: isDraft
            ? child3Counts.nextVacantCount
            : child3Counts.currentVacantCount,
          children: this.buildOrgChild4s(data.orgChild4s, positionCounts, isDraft),
        };
        return new HttpSuccess([formattedData]);
      }
      case 5: {
        const data = await this.child4Repository.findOne({
          where: { id: idNode },
          relations: ["orgRevision"],
        });
        if (!data) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4Id");
        }

        const child4Counts = getPositionCount(positionCounts.orgChild4Map, data.id);
        const formattedData = {
          departmentName: data.orgChild4Name,
          deptID: data.id,
          type: 5,
          totalPositionCount: child4Counts.totalCount,
          totalPositionVacant: isDraft
            ? child4Counts.nextVacantCount
            : child4Counts.currentVacantCount,
        };
        return new HttpSuccess([formattedData]);
      }
      default:
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: ");
    }
  }

  /**
   * API เช็ค node
   *
   * @summary  เช็ค node (ADMIN)
   *
   */
  @Post("find/node")
  async findNodeAll(@Body() requestBody: { node: number; nodeId: string }) {
    switch (requestBody.node) {
      case 0: {
        const data = await this.orgRootRepository.findOne({
          where: { id: requestBody.nodeId },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId.");
        }
        return new HttpSuccess([data.id]);
      }
      case 1: {
        const data = await this.child1Repository.findOne({
          where: { id: requestBody.nodeId },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1.");
        }
        return new HttpSuccess([data.orgRootId, data.id]);
      }
      case 2: {
        const data = await this.child2Repository.findOne({
          where: { id: requestBody.nodeId },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2.");
        }
        return new HttpSuccess([data.orgRootId, data.orgChild1Id, data.id]);
      }
      case 3: {
        const data = await this.child3Repository.findOne({
          where: { id: requestBody.nodeId },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3.");
        }
        return new HttpSuccess([data.orgRootId, data.orgChild1Id, data.orgChild2Id, data.id]);
      }
      case 4: {
        const data = await this.child4Repository.findOne({
          where: { id: requestBody.nodeId },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4.");
        }
        return new HttpSuccess([
          data.orgRootId,
          data.orgChild1Id,
          data.orgChild2Id,
          data.orgChild3Id,
          data.id,
        ]);
      }
      default:
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.node);
    }
  }

  /**
   * API เช็ค node
   *
   * @summary  เช็ค node (ADMIN)
   *
   */
  @Post("find/node-all")
  async findNodeAllOrg(@Body() requestBody: { node: number | null; nodeId: string | null }) {
    let orgRootRankSub1 = ["BUREAU", "OFFICE"];
    let orgRootRankSub2 = ["DISTRICT"];
    let data1: any;
    let data2: any;
    switch (requestBody.node) {
      case 0: {
        const _data1 = await this.orgRootRepository.find({
          where: {
            id: requestBody.nodeId ?? "",
            orgRootRankSub: In(orgRootRankSub1),
          },
          order: { orgRootOrder: "ASC" },
        });
        const _data2 = await this.orgRootRepository.find({
          where: [
            {
              id: requestBody.nodeId ?? "",
              orgRootRankSub: In(orgRootRankSub2),
            },
            {
              id: requestBody.nodeId ?? "",
              orgRootRankSub: IsNull(),
            },
          ],
          order: { orgRootOrder: "ASC" },
        });
        data1 = _data1.map((y) => ({
          name: y.orgRootName,
          rootId: y.id,
          rootDnaId: y.ancestorDNA,
          child1Id: null,
          child1DnaId: null,
          child2Id: null,
          child2DnaId: null,
          child3Id: null,
          child3DnaId: null,
          child4Id: null,
          child4DnaId: null,
        }));
        data2 = _data2.map((y) => ({
          name: y.orgRootName,
          rootId: y.id,
          rootDnaId: y.ancestorDNA,
          child1Id: null,
          child1DnaId: null,
          child2Id: null,
          child2DnaId: null,
          child3Id: null,
          child3DnaId: null,
          child4Id: null,
          child4DnaId: null,
        }));
        break;
      }
      case 1: {
        const _data1 = await this.child1Repository.find({
          where: {
            id: requestBody.nodeId ?? "",
            orgRoot: {
              orgRootRankSub: In(orgRootRankSub1),
            },
          },
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot"],
        });
        const _data2 = await this.child1Repository.find({
          where: [
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: In(orgRootRankSub2),
              },
            },
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: IsNull(),
              },
            },
          ],
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot"],
        });
        data1 = _data1.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.id,
          child1DnaId: y.ancestorDNA,
          child2Id: null,
          child2DnaId: null,
          child3Id: null,
          child3DnaId: null,
          child4Id: null,
          child4DnaId: null,
        }));
        data2 = _data2.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.id,
          child1DnaId: y.ancestorDNA,
          child2Id: null,
          child2DnaId: null,
          child3Id: null,
          child3DnaId: null,
          child4Id: null,
          child4DnaId: null,
        }));
        break;
      }
      case 2: {
        const _data1 = await this.child2Repository.find({
          where: {
            id: requestBody.nodeId ?? "",
            orgRoot: {
              orgRootRankSub: In(orgRootRankSub1),
            },
          },
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot", "orgChild1"],
        });
        const _data2 = await this.child2Repository.find({
          where: [
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: In(orgRootRankSub2),
              },
            },
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: IsNull(),
              },
            },
          ],
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot", "orgChild1"],
        });
        data1 = _data1.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.orgChild1.id,
          child1DnaId: y.orgChild1.ancestorDNA,
          child2Id: y.id,
          child2DnaId: y.ancestorDNA,
          child3Id: null,
          child3DnaId: null,
          child4Id: null,
          child4DnaId: null,
        }));
        data2 = _data2.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.orgChild1.id,
          child1DnaId: y.orgChild1.ancestorDNA,
          child2Id: y.id,
          child2DnaId: y.ancestorDNA,
          child3Id: null,
          child3DnaId: null,
          child4Id: null,
          child4DnaId: null,
        }));
        break;
      }
      case 3: {
        const _data1 = await this.child3Repository.find({
          where: {
            id: requestBody.nodeId ?? "",
            orgRoot: {
              orgRootRankSub: In(orgRootRankSub1),
            },
          },
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot", "orgChild1", "orgChild2"],
        });
        const _data2 = await this.child3Repository.find({
          where: [
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: In(orgRootRankSub2),
              },
            },
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: IsNull(),
              },
            },
          ],
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot", "orgChild1", "orgChild2"],
        });
        data1 = _data1.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.orgChild1.id,
          child1DnaId: y.orgChild1.ancestorDNA,
          child2Id: y.orgChild2.id,
          child2DnaId: y.orgChild2.ancestorDNA,
          child3Id: y.id,
          child3DnaId: y.ancestorDNA,
          child4Id: null,
          child4DnaId: null,
        }));
        data2 = _data2.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.orgChild1.id,
          child1DnaId: y.orgChild1.ancestorDNA,
          child2Id: y.orgChild2.id,
          child2DnaId: y.orgChild2.ancestorDNA,
          child3Id: y.id,
          child3DnaId: y.ancestorDNA,
          child4Id: null,
          child4DnaId: null,
        }));
        break;
      }
      case 4: {
        const _data1 = await this.child4Repository.find({
          where: {
            id: requestBody.nodeId ?? "",
            orgRoot: {
              orgRootRankSub: In(orgRootRankSub1),
            },
          },
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3"],
        });
        const _data2 = await this.child4Repository.find({
          where: [
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: In(orgRootRankSub2),
              },
            },
            {
              id: requestBody.nodeId ?? "",
              orgRoot: {
                orgRootRankSub: IsNull(),
              },
            },
          ],
          order: {
            orgRoot: { orgRootOrder: "ASC" },
          },
          relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3"],
        });
        data1 = _data1.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.orgChild1.id,
          child1DnaId: y.orgChild1.ancestorDNA,
          child2Id: y.orgChild2.id,
          child2DnaId: y.orgChild2.ancestorDNA,
          child3Id: y.orgChild3.id,
          child3DnaId: y.orgChild3.ancestorDNA,
          child4Id: y.id,
          child4DnaId: y.ancestorDNA,
        }));
        data2 = _data2.map((y) => ({
          name: y.orgRoot.orgRootName,
          rootId: y.orgRoot.id,
          rootDnaId: y.orgRoot.ancestorDNA,
          child1Id: y.orgChild1.id,
          child1DnaId: y.orgChild1.ancestorDNA,
          child2Id: y.orgChild2.id,
          child2DnaId: y.orgChild2.ancestorDNA,
          child3Id: y.orgChild3.id,
          child3DnaId: y.orgChild3.ancestorDNA,
          child4Id: y.id,
          child4DnaId: y.ancestorDNA,
        }));
        break;
      }
      default: {
        const orgRevision = await this.orgRevisionRepository.findOne({
          where: {
            orgRevisionIsCurrent: true,
            orgRevisionIsDraft: false,
          },
        });
        const _data1 = await this.orgRootRepository.find({
          where: {
            orgRevisionId: orgRevision?.id,
            orgRootRankSub: In(orgRootRankSub1),
          },
          order: { orgRootOrder: "ASC" },
        });
        const _data2 = await this.orgRootRepository.find({
          where: [
            {
              orgRevisionId: orgRevision?.id,
              orgRootRankSub: In(orgRootRankSub2),
            },
            {
              orgRevisionId: orgRevision?.id,
              orgRootRankSub: IsNull(),
            },
          ],
          order: { orgRootOrder: "ASC" },
        });
        data1 = _data1.map((y) => ({
          name: y.orgRootName,
          rootId: y.id,
          rootDnaId: y.ancestorDNA,
          child1Id: null,
          child2Id: null,
          child3Id: null,
          child4Id: null,
        }));
        data2 = _data2.map((y) => ({
          name: y.orgRootName,
          rootId: y.id,
          rootDnaId: y.ancestorDNA,
          child1Id: null,
          child2Id: null,
          child3Id: null,
          child4Id: null,
        }));
      }
    }
    return new HttpSuccess({ isRootTrue: data1, isRootFalse: data2 });
  }

  /**
   * API เช็ค node detail
   *
   * @summary  เช็ค node detail (ADMIN)
   *
   */
  @Post("find/all")
  async findNodeAllDetail(@Body() requestBody: { node: number; nodeId: string }) {
    switch (requestBody.node) {
      case 0: {
        const data = await this.orgRootRepository.findOne({
          where: { id: requestBody.nodeId },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId.");
        }
        return new HttpSuccess({
          rootId: data.id,
          rootDnaId: data.ancestorDNA,
          root: data.orgRootName,
          rootShortName: data.orgRootShortName,
        });
      }
      case 1: {
        const data = await this.child1Repository.findOne({
          where: { id: requestBody.nodeId },
          relations: {
            orgRoot: true,
          },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.id,
          child1DnaId: data.ancestorDNA,
          child1: data.orgChild1Name,
          child1ShortName: data.orgChild1ShortName,
        });
      }
      case 2: {
        const data = await this.child2Repository.findOne({
          where: { id: requestBody.nodeId },
          relations: {
            orgRoot: true,
            orgChild1: true,
          },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.orgChild1Id,
          child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA,
          child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name,
          child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName,
          child2Id: data.id,
          child2DnaId: data.ancestorDNA,
          child2: data.orgChild2Name,
          child2ShortName: data.orgChild2ShortName,
        });
      }
      case 3: {
        const data = await this.child3Repository.findOne({
          where: { id: requestBody.nodeId },
          relations: {
            orgRoot: true,
            orgChild1: true,
            orgChild2: true,
          },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.orgChild1Id,
          child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA,
          child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name,
          child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName,
          child2Id: data.orgChild2Id,
          child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA,
          child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name,
          child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName,
          child3Id: data.id,
          child3DnaId: data.ancestorDNA,
          child3: data.orgChild3Name,
          child3ShortName: data.orgChild3ShortName,
        });
      }
      case 4: {
        const data = await this.child4Repository.findOne({
          where: { id: requestBody.nodeId },
          relations: {
            orgRoot: true,
            orgChild1: true,
            orgChild2: true,
            orgChild3: true,
          },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.orgChild1Id,
          child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA,
          child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name,
          child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName,
          child2Id: data.orgChild2Id,
          child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA,
          child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name,
          child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName,
          child3Id: data.orgChild3Id,
          child3DnaId: data.orgChild3 == null ? null : data.orgChild3.ancestorDNA,
          child3: data.orgChild3 == null ? null : data.orgChild3.orgChild3Name,
          child3ShortName: data.orgChild3 == null ? null : data.orgChild3.orgChild3ShortName,
          child4Id: data.id,
          child4DnaId: data.ancestorDNA,
          child4: data.orgChild4Name,
          child4ShortName: data.orgChild4ShortName,
        });
      }
      default:
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.node);
    }
  }

  /**
   * API เช็ค node detail
   *
   * @summary  เช็ค node detail (ADMIN)
   *
   */
  @Post("find/allv2")
  async findNodeAllDetailV2(@Body() requestBody: { node: number; nodeId: string }) {
    switch (requestBody.node) {
      case 0: {
        const data = await this.orgRootRepository.findOne({
          where: { ancestorDNA: requestBody.nodeId },
          order: { createdAt: "DESC" },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId.");
        }
        return new HttpSuccess({
          rootId: data.id,
          rootDnaId: data.ancestorDNA,
          root: data.orgRootName,
          rootShortName: data.orgRootShortName,
        });
      }
      case 1: {
        const data = await this.child1Repository.findOne({
          where: { ancestorDNA: requestBody.nodeId },
          relations: {
            orgRoot: true,
          },
          order: { createdAt: "DESC" },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.id,
          child1DnaId: data.ancestorDNA,
          child1: data.orgChild1Name,
          child1ShortName: data.orgChild1ShortName,
        });
      }
      case 2: {
        const data = await this.child2Repository.findOne({
          where: { ancestorDNA: requestBody.nodeId },
          relations: {
            orgRoot: true,
            orgChild1: true,
          },
          order: { createdAt: "DESC" },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.orgChild1Id,
          child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA,
          child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name,
          child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName,
          child2Id: data.id,
          child2DnaId: data.ancestorDNA,
          child2: data.orgChild2Name,
          child2ShortName: data.orgChild2ShortName,
        });
      }
      case 3: {
        const data = await this.child3Repository.findOne({
          where: { ancestorDNA: requestBody.nodeId },
          relations: {
            orgRoot: true,
            orgChild1: true,
            orgChild2: true,
          },
          order: { createdAt: "DESC" },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.orgChild1Id,
          child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA,
          child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name,
          child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName,
          child2Id: data.orgChild2Id,
          child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA,
          child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name,
          child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName,
          child3Id: data.id,
          child3DnaId: data.ancestorDNA,
          child3: data.orgChild3Name,
          child3ShortName: data.orgChild3ShortName,
        });
      }
      case 4: {
        const data = await this.child4Repository.findOne({
          where: { ancestorDNA: requestBody.nodeId },
          relations: {
            orgRoot: true,
            orgChild1: true,
            orgChild2: true,
            orgChild3: true,
          },
          order: { createdAt: "DESC" },
        });
        if (data == null) {
          throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4.");
        }
        return new HttpSuccess({
          rootId: data.orgRootId,
          rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA,
          root: data.orgRoot == null ? null : data.orgRoot.orgRootName,
          rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName,
          child1Id: data.orgChild1Id,
          child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA,
          child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name,
          child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName,
          child2Id: data.orgChild2Id,
          child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA,
          child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name,
          child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName,
          child3Id: data.orgChild3Id,
          child3DnaId: data.orgChild3 == null ? null : data.orgChild3.ancestorDNA,
          child3: data.orgChild3 == null ? null : data.orgChild3.orgChild3Name,
          child3ShortName: data.orgChild3 == null ? null : data.orgChild3.orgChild3ShortName,
          child4Id: data.id,
          child4DnaId: data.ancestorDNA,
          child4: data.orgChild4Name,
          child4ShortName: data.orgChild4ShortName,
        });
      }
      default:
        throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.node);
    }
  }

  /**
   * API หาสำนักทั้งหมด
   *
   * @summary หาสำนักทั้งหมด
   *
   */
  @Get("active/root")
  async GetActiveRoot() {
    const orgRevisionActive = await this.orgRevisionRepository.findOne({
      where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
    });
    if (!orgRevisionActive) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้");
    }

    const data = await this.orgRootRepository.find({
      where: { orgRevisionId: orgRevisionActive.id },
      relations: [
        "orgRevision",
        "orgChild1s",
        "orgChild1s.orgChild2s",
        "orgChild1s.orgChild2s.orgChild3s",
        "orgChild1s.orgChild2s.orgChild3s.orgChild4s",
      ],
      order: {
        orgChild1s: {
          orgChild1Order: "ASC",
          orgChild2s: {
            orgChild2Order: "ASC",
            orgChild3s: {
              orgChild3Order: "ASC",
              orgChild4s: {
                orgChild4Order: "ASC",
              },
            },
          },
        },
      },
    });
    return new HttpSuccess(data);
  }

  /**
   * API หาสำนักทั้งหมด
   *
   * @summary หาสำนักทั้งหมด
   *
   */
  @Get("active/root/id")
  async GetActiveRootId() {
    const orgRevisionActive = await this.orgRevisionRepository.findOne({
      where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
    });
    if (!orgRevisionActive) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้");
    }

    const data = await this.orgRootRepository.find({
      where: { orgRevisionId: orgRevisionActive.id },
      order: {
        orgRootOrder: "ASC",
      },
    });
    return new HttpSuccess(data.map((x) => x.id));
  }

  /**
   * API หาสำนักทั้งหมด
   *
   * @summary หาสำนักทั้งหมด
   *
   */
  @Get("active/root/all")
  async GetActiveRootAll() {
    const orgRevisionActive = await this.orgRevisionRepository.findOne({
      where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
    });
    if (!orgRevisionActive) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้");
    }

    const data = await this.orgRootRepository.find({
      where: {
        orgRevisionId: orgRevisionActive.id,
        id: In([
          "d7e98989-b5ce-47d6-93c3-ab63ed486348",
          "6f9b30e1-757a-40d5-b053-61eb1b91c0f0",
          "eaf65f33-25e9-4956-9dba-5d909f5eb595",
          "87c5bc89-300c-4b6a-99e4-26371436caa2",
          "982d33af-4eb5-4cc8-9c9f-b3ccadbb66d7",
          "e0545eca-5d0a-4a1c-8bbd-e3e25c2521db",
        ]),
      },
      order: {
        orgRootOrder: "ASC",
      },
    });
    return new HttpSuccess(data);
  }

  /**
   * API
   *
   * @summary
   *
   */
  @Get("active/root/latest")
  async GetActiveRootIdLatest() {
    const orgRevisionActive = await this.orgRevisionRepository.findOne({
      where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
    });
    if (!orgRevisionActive) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้");
    }
    return new HttpSuccess(orgRevisionActive.id);
  }

  /**
   * API หาสำนักทั้งหมด by revision
   *
   * @summary หาสำนักทั้งหมด by revision
   *
   */
  @Get("active/root/{revisionId}")
  async GetActiveRootByRevision(@Path() revisionId: string) {
    const rawData = await this.orgRootRepository.find({
      where: { orgRevisionId: revisionId },
      relations: [
        "orgRevision",
        "orgChild1s",
        "orgChild1s.orgChild2s",
        "orgChild1s.orgChild2s.orgChild3s",
        "orgChild1s.orgChild2s.orgChild3s.orgChild4s",
      ],
      order: {
        orgRootOrder: "ASC",
        orgChild1s: {
          orgChild1Order: "ASC",
          orgChild2s: {
            orgChild2Order: "ASC",
            orgChild3s: {
              orgChild3Order: "ASC",
              orgChild4s: {
                orgChild4Order: "ASC",
              },
            },
          },
        },
      },
    });
    const data = rawData.map((item) => ({
      ...item,
      orgRootCode: item.orgRootCode + "00",
    }));
    return new HttpSuccess(data);
  }
  /**
   * API หา revision ล่าสุด
   *
   * @summary หา revision ล่าสุด
   *
   */
  @Get("revision/latest")
  async salaryGen() {
    const findRevision = await this.orgRevisionRepository.findOne({
      where: { orgRevisionIsCurrent: true },
    });
    if (!findRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างล่าสุด");
    }
    return new HttpSuccess(findRevision.id);
  }

  /**
   * API รายละเอียดโครงสร้าง
   *
   * @summary รายละเอียดโครงสร้าง (ADMIN)
   *
   */
  @Get("act/{id}")
  async detailAct(@Path() id: string, @Request() request: RequestWithUser) {
    let _data: OrgPermissionData = {
      root: null,
      child1: null,
      child2: null,
      child3: null,
      child4: null,
    };
    // if (!request.user.role.includes("SUPER_ADMIN")) {
    // _data = await new permission().PermissionOrgList(request, "SYS_ACTING");
    // }
    const _privilege = await new permission().PermissionOrgList(request, "SYS_ACTING");

    const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }

    const attrOwnership = _privilege.root === null ? true : false;

    const profile = await this.profileRepo.findOne({
      where: { keycloak: request.user.sub },
      relations: ["permissionProfiles", "current_holders", "current_holders.posMasterAssigns"],
    });
    if (!profile) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ");
    }
    let profileAssign = profile.current_holders
      ?.find((x) => x.orgRevisionId === id)
      ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG");

    if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) {
      if (Array.isArray(profile.permissionProfiles) && profile.permissionProfiles.length > 0) {
        _data.root = profile.permissionProfiles.map((x) => x.orgRootId);
      } else {
        return new HttpSuccess({ remark: "", data: [] });
      }
    }
    // กำหนดการเข้าถึงข้อมูลตามสถานะและสิทธิ์
    const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent;
    if (isCurrentActive) {
      if (profileAssign && _privilege.privilege !== "OWNER" && _privilege.privilege !== "PARENT") {
        if (_privilege.privilege == "NORMAL") {
          const holder = profile.current_holders.find((x) => x.orgRevisionId === id);
          if (!holder) return new HttpSuccess({ remark: "", data: [] });
          _data.root = [holder.orgRootId];
          _data.child1 = [holder.orgChild1Id];
          _data.child2 = [holder.orgChild2Id];
          _data.child3 = [holder.orgChild3Id];
          _data.child4 = [holder.orgChild4Id];
        } else if (_privilege.privilege == "CHILD" || _privilege.privilege == "BROTHER") {
          const holder = profile.current_holders.find((x) => x.orgRevisionId === id);
          if (!holder) return new HttpSuccess({ remark: "", data: [] });
          _data.root = [holder.orgRootId];
          if (_privilege.root && _privilege.child1 === null) {
          } else if (_privilege.child1 && _privilege.child2 === null) {
            _data.child1 = [holder.orgChild1Id];
          } else if (_privilege.child2 && _privilege.child3 === null) {
            _data.child1 = [holder.orgChild1Id];
            _data.child2 = [holder.orgChild2Id];
          } else if (_privilege.child3 && _privilege.child4 === null) {
            _data.child1 = [holder.orgChild1Id];
            _data.child2 = [holder.orgChild2Id];
            _data.child3 = [holder.orgChild3Id];
            _data.child4 = [holder.orgChild4Id];
          }
        } else {
          _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId];
        }
      } else {
        if (!attrOwnership) _data = _privilege;
      }
    }

    const orgDna = await new permission().checkDna(request, request.user.sub);
    let level: NodeLevel = resolveNodeLevel(orgDna);

    const orgRootData = await AppDataSource.getRepository(OrgRoot)
      .createQueryBuilder("orgRoot")
      .select([
        "orgRoot.id",
        "orgRoot.orgRootName",
        "orgRoot.orgRootShortName",
        "orgRoot.orgRootCode",
        "orgRoot.orgRootOrder",
      ])
      .addSelect([
        "posMasters.id",
        "posMasters.posMasterNo",
        "posMasters.orgChild1Id",
        "posMasters.isDirector",
      ])
      .addSelect(["current_holder.prefix", "current_holder.firstName", "current_holder.lastName"])
      .where("orgRoot.orgRevisionId = :id", { id })
      .andWhere(
        _data.root != undefined && _data.root != null
          ? _data.root[0] != null
            ? `orgRoot.id IN (:...node)`
            : `orgRoot.id is null`
          : "1=1",
        {
          node: _data.root,
        },
      )
      .leftJoin("orgRoot.posMasters", "posMasters")
      .leftJoin("posMasters.current_holder", "current_holder")
      .orderBy("orgRoot.orgRootOrder", "ASC")
      .addOrderBy("posMasters.posMasterOrder", "ASC")
      .getMany();

    const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null;
    const orgChild1Data =
      orgRootIds && orgRootIds.length > 0
        ? await AppDataSource.getRepository(OrgChild1)
            .createQueryBuilder("orgChild1")
            .select([
              "orgChild1.id",
              "orgChild1.orgRootId",
              "orgChild1.orgChild1Name",
              "orgChild1.orgChild1ShortName",
              "orgChild1.orgChild1Code",
              "orgChild1.orgChild1Order",
            ])
            .addSelect([
              "posMasters.id",
              "posMasters.posMasterNo",
              "posMasters.orgChild2Id",
              "posMasters.isDirector",
            ])
            .addSelect([
              "current_holder.prefix",
              "current_holder.firstName",
              "current_holder.lastName",
            ])
            .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds })
            .andWhere(
              _data.child1 != undefined && _data.child1 != null
                ? _data.child1[0] != null
                  ? `orgChild1.id IN (:...node)`
                  : `orgChild1.id is null`
                : "1=1",
              {
                node: _data.child1,
              },
            )
            .leftJoin("orgChild1.posMasters", "posMasters")
            .leftJoin("posMasters.current_holder", "current_holder")
            .orderBy("orgChild1.orgChild1Order", "ASC")
            .addOrderBy("posMasters.posMasterOrder", "ASC")
            .getMany()
        : [];

    const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null;
    const orgChild2Data =
      orgChild1Ids && orgChild1Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild2)
            .createQueryBuilder("orgChild2")
            .select([
              "orgChild2.id",
              "orgChild2.orgChild1Id",
              "orgChild2.orgChild2Name",
              "orgChild2.orgChild2ShortName",
              "orgChild2.orgChild2Code",
              "orgChild2.orgChild2Order",
            ])
            .addSelect([
              "posMasters.id",
              "posMasters.posMasterNo",
              "posMasters.orgChild3Id",
              "posMasters.isDirector",
            ])
            .addSelect([
              "current_holder.prefix",
              "current_holder.firstName",
              "current_holder.lastName",
            ])
            .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids })
            .andWhere(
              _data.child2 != undefined && _data.child2 != null
                ? _data.child2[0] != null
                  ? `orgChild2.id IN (:...node)`
                  : `orgChild2.id is null`
                : "1=1",
              {
                node: _data.child2,
              },
            )
            .leftJoin("orgChild2.posMasters", "posMasters")
            .leftJoin("posMasters.current_holder", "current_holder")
            .orderBy("orgChild2.orgChild2Order", "ASC")
            .addOrderBy("posMasters.posMasterOrder", "ASC")
            .getMany()
        : [];

    const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null;
    const orgChild3Data =
      orgChild2Ids && orgChild2Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild3)
            .createQueryBuilder("orgChild3")
            .select([
              "orgChild3.id",
              "orgChild3.orgChild2Id",
              "orgChild3.orgChild3Name",
              "orgChild3.orgChild3ShortName",
              "orgChild3.orgChild3Code",
              "orgChild3.orgChild3Order",
            ])
            .addSelect([
              "posMasters.id",
              "posMasters.posMasterNo",
              "posMasters.orgChild4Id",
              "posMasters.isDirector",
            ])
            .addSelect([
              "current_holder.prefix",
              "current_holder.firstName",
              "current_holder.lastName",
            ])
            .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids })
            .andWhere(
              _data.child3 != undefined && _data.child3 != null
                ? _data.child3[0] != null
                  ? `orgChild3.id IN (:...node)`
                  : `orgChild3.id is null`
                : "1=1",
              {
                node: _data.child3,
              },
            )
            .leftJoin("orgChild3.posMasters", "posMasters")
            .leftJoin("posMasters.current_holder", "current_holder")
            .orderBy("orgChild3.orgChild3Order", "ASC")
            .addOrderBy("posMasters.posMasterOrder", "ASC")
            .getMany()
        : [];

    const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null;
    const orgChild4Data =
      orgChild3Ids && orgChild3Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild4)
            .createQueryBuilder("orgChild4")
            .select([
              "orgChild4.id",
              "orgChild4.orgChild3Id",
              "orgChild4.orgChild4Name",
              "orgChild4.orgChild4ShortName",
              "orgChild4.orgChild4Code",
              "orgChild4.orgChild4Order",
            ])
            .addSelect(["posMasters.id", "posMasters.posMasterNo", "posMasters.isDirector"])
            .addSelect([
              "current_holder.prefix",
              "current_holder.firstName",
              "current_holder.lastName",
            ])
            .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids })
            .andWhere(
              _data.child4 != undefined && _data.child4 != null
                ? _data.child4[0] != null
                  ? `orgChild4.id IN (:...node)`
                  : `orgChild4.id is null`
                : "1=1",
              {
                node: _data.child4,
              },
            )
            .leftJoin("orgChild4.posMasters", "posMasters")
            .leftJoin("posMasters.current_holder", "current_holder")
            .orderBy("orgChild4.orgChild4Order", "ASC")
            .addOrderBy("posMasters.posMasterOrder", "ASC")
            .getMany()
        : [];

    const cannotViewRootPosMaster =
      _privilege.privilege === "PARENT" ||
      (_privilege.privilege === "BROTHER" && level !== null && level > 1) ||
      (_privilege.privilege === "CHILD" && level !== null && level > 0) ||
      (_privilege.privilege === "NORMAL" && level !== null && level !== 0);

    const cannotViewChild1PosMaster =
      (_privilege.privilege === "PARENT" && level !== null && level > 1) ||
      (_privilege.privilege === "BROTHER" && level !== null && level > 2) ||
      (_privilege.privilege === "CHILD" && level !== null && level > 1) ||
      (_privilege.privilege === "NORMAL" && level !== 1);

    const cannotViewChild2PosMaster =
      (_privilege.privilege === "PARENT" && level !== null && level > 2) ||
      (_privilege.privilege === "BROTHER" && level !== null && level > 3) ||
      (_privilege.privilege === "CHILD" && level !== null && level > 2) ||
      (_privilege.privilege === "NORMAL" && level !== 2);

    const cannotViewChild3PosMaster =
      (_privilege.privilege === "PARENT" && level !== null && level > 3) ||
      (_privilege.privilege === "BROTHER" && level !== null && level > 4) ||
      (_privilege.privilege === "CHILD" && level !== null && level > 3) ||
      (_privilege.privilege === "NORMAL" && level !== 3);

    const cannotViewChild4PosMaster =
      (_privilege.privilege === "PARENT" && level !== null && level > 4) ||
      (_privilege.privilege === "CHILD" && level !== null && level > 4) ||
      (_privilege.privilege === "NORMAL" && level !== 4);

    const formattedData = orgRootData.map((orgRoot) => ({
      orgTreeId: orgRoot.id,
      orgLevel: 0,
      orgName: orgRoot.orgRootName,
      orgTreeName: orgRoot.orgRootName,
      orgTreeShortName: orgRoot.orgRootShortName,
      orgTreeCode: orgRoot.orgRootCode,
      orgCode: orgRoot.orgRootCode + "00",
      orgRootName: orgRoot.orgRootName,
      labelName: generateLabelName(
        orgRoot.orgRootName,
        orgRoot.orgRootCode + "00",
        orgRoot.orgRootShortName,
        orgRoot.orgRootName,
        orgRoot.orgRootCode,
        orgRoot.orgRootShortName,
      ),
      posMaster: cannotViewRootPosMaster
        ? []
        : filterPosMasters(orgRoot.posMasters, "orgChild1Id").map((x) =>
            formatPosMaster(x, orgRoot.orgRootShortName, orgRoot.id, 0),
          ),
      children: orgChild1Data
        .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id)
        .map((orgChild1) => ({
          orgTreeId: orgChild1.id,
          orgRootId: orgRoot.id,
          orgLevel: 1,
          orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
          orgTreeName: orgChild1.orgChild1Name,
          orgTreeShortName: orgChild1.orgChild1ShortName,
          orgTreeCode: orgChild1.orgChild1Code,
          orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code,
          orgRootName: orgRoot.orgRootName,
          labelName: generateLabelName(
            orgChild1.orgChild1Name,
            orgChild1.orgChild1Code,
            orgChild1.orgChild1ShortName,
            orgRoot.orgRootName,
            orgRoot.orgRootCode,
            orgRoot.orgRootShortName,
          ),
          posMaster: cannotViewChild1PosMaster
            ? []
            : filterPosMasters(orgChild1.posMasters, "orgChild2Id").map((x) =>
                formatPosMaster(x, orgChild1.orgChild1ShortName, orgChild1.id, 1),
              ),
          children: orgChild2Data
            .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id)
            .map((orgChild2) => ({
              orgTreeId: orgChild2.id,
              orgRootId: orgChild1.id,
              orgLevel: 2,
              orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
              orgTreeName: orgChild2.orgChild2Name,
              orgTreeShortName: orgChild2.orgChild2ShortName,
              orgTreeCode: orgChild2.orgChild2Code,
              orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code,
              orgRootName: orgRoot.orgRootName,
              labelName: generateLabelName(
                orgChild2.orgChild2Name,
                orgChild2.orgChild2Code,
                orgChild2.orgChild2ShortName,
                orgRoot.orgRootName,
                orgRoot.orgRootCode,
                orgRoot.orgRootShortName,
                [orgChild1.orgChild1Name],
                [orgChild1.orgChild1Code],
                [orgChild1.orgChild1ShortName],
              ),
              posMaster: cannotViewChild2PosMaster
                ? []
                : filterPosMasters(orgChild2.posMasters, "orgChild3Id").map((x) =>
                    formatPosMaster(x, orgChild2.orgChild2ShortName, orgChild2.id, 2),
                  ),
              children: orgChild3Data
                .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id)
                .map((orgChild3) => ({
                  orgTreeId: orgChild3.id,
                  orgRootId: orgChild2.id,
                  orgLevel: 3,
                  orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                  orgTreeName: orgChild3.orgChild3Name,
                  orgTreeShortName: orgChild3.orgChild3ShortName,
                  orgTreeCode: orgChild3.orgChild3Code,
                  orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code,
                  orgRootName: orgRoot.orgRootName,
                  labelName: generateLabelName(
                    orgChild3.orgChild3Name,
                    orgChild3.orgChild3Code,
                    orgChild3.orgChild3ShortName,
                    orgRoot.orgRootName,
                    orgRoot.orgRootCode,
                    orgRoot.orgRootShortName,
                    [orgChild2.orgChild2Name, orgChild1.orgChild1Name],
                    [orgChild2.orgChild2Code, orgChild1.orgChild1Code],
                    [orgChild2.orgChild2ShortName, orgChild1.orgChild1ShortName],
                  ),
                  posMaster: cannotViewChild3PosMaster
                    ? []
                    : filterPosMasters(orgChild3.posMasters, "orgChild4Id").map((x) =>
                        formatPosMaster(x, orgChild3.orgChild3ShortName, orgChild3.id, 3),
                      ),
                  children: orgChild4Data
                    .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id)
                    .map((orgChild4) => ({
                      orgTreeId: orgChild4.id,
                      orgRootId: orgChild3.id,
                      orgLevel: 4,
                      orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`,
                      orgTreeName: orgChild4.orgChild4Name,
                      orgTreeShortName: orgChild4.orgChild4ShortName,
                      orgTreeCode: orgChild4.orgChild4Code,
                      orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code,
                      orgRootName: orgRoot.orgRootName,
                      labelName: generateLabelName(
                        orgChild4.orgChild4Name,
                        orgChild4.orgChild4Code,
                        orgChild4.orgChild4ShortName,
                        orgRoot.orgRootName,
                        orgRoot.orgRootCode,
                        orgRoot.orgRootShortName,
                        [orgChild3.orgChild3Name, orgChild2.orgChild2Name, orgChild1.orgChild1Name],
                        [orgChild3.orgChild3Code, orgChild2.orgChild2Code, orgChild1.orgChild1Code],
                        [
                          orgChild3.orgChild3ShortName,
                          orgChild2.orgChild2ShortName,
                          orgChild1.orgChild1ShortName,
                        ],
                      ),
                      posMaster: cannotViewChild4PosMaster
                        ? []
                        : orgChild4.posMasters
                            .filter((x) => x.isDirector === true)
                            .map((x) =>
                              formatPosMaster(x, orgChild4.orgChild4ShortName, orgChild4.id, 4),
                            ),
                    })),
                })),
            })),
        })),
    }));

    return new HttpSuccess(formattedData);
  }

  /**
   * API
   *
   * @summary  (ADMIN)
   *
   * @param {string} id
   */
  @Get("approver/{id}")
  async getUserRootOrg(@Path() id: string, @Request() request: RequestWithUser) {
    if (id == "00000000-0000-0000-0000-000000000000") {
      const maps = {
        id: "00000000-0000-0000-0000-000000000000",
        name: "",
        positionName: "ปลัดกรุงเทพมหานคร",
      };
      return new HttpSuccess(maps);
    }

    const root = await this.orgRootRepository.findOne({
      where: { id: id },
    });
    if (!root) throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Root");
    const posMaster = await this.posMasterRepository.find({
      where: { orgRootId: root.id, orgChild1Id: IsNull(), current_holder: Not(IsNull()) },
      relations: ["current_holder"],
    });
    if (!posMaster) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่ง");

    const maps = posMaster.map((posMaster) => ({
      id: posMaster?.current_holder?.id,
      name: `${posMaster?.current_holder?.prefix}${posMaster?.current_holder?.firstName} ${posMaster?.current_holder?.lastName}`,
      positionName: posMaster?.current_holder?.position,
    }));

    return new HttpSuccess(maps);
  }

  /**
   * API รายละเอียดโครงสร้าง
   *
   * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25
   *
   */
  @Get("system/{id}/{system}")
  async detailBySystem(
    @Path() id: string,
    @Path() system: string,
    @Request() request: RequestWithUser,
  ) {
    const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }
    let _data: any = {
      root: null,
      child1: null,
      child2: null,
      child3: null,
      child4: null,
    };
    if (
      (orgRevision.orgRevisionIsDraft == true && orgRevision.orgRevisionIsCurrent == false) ||
      system != "SYS_ORG"
    ) {
      _data = await new permission().PermissionOrgList(request, system.trim().toUpperCase());
    }

    const profile = await this.profileRepo.findOne({
      where: { keycloak: request.user.sub },
      relations: ["permissionProfiles", "current_holders"],
    });

    if (!profile) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ");
    }

    let _privilege = await new permission().PermissionOrgList(request, system);
    const attrOwnership = _privilege.root === null ? true : false;

    if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) {
      if (Array.isArray(profile.permissionProfiles) && profile.permissionProfiles.length > 0) {
        _data.root = profile.permissionProfiles.map((x) => x.orgRootId);
      } else {
        return new HttpSuccess({ remark: "", data: [] });
      }
    }

    // กำหนดการเข้าถึงข้อมูลตามสถานะและสิทธิ์
    const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent;
    if (isCurrentActive) {
      if (_privilege.privilege !== "OWNER" && _privilege.privilege !== "PARENT") {
        if (_privilege.privilege == "NORMAL") {
          const holder = profile.current_holders.find((x) => x.orgRevisionId === id);
          if (!holder) return;
          _data.root = [holder.orgRootId];
          _data.child1 = [holder.orgChild1Id];
          _data.child2 = [holder.orgChild2Id];
          _data.child3 = [holder.orgChild3Id];
          _data.child4 = [holder.orgChild4Id];
        } else if (_privilege.privilege == "CHILD" || _privilege.privilege == "BROTHER") {
          const holder = profile.current_holders.find((x) => x.orgRevisionId === id);
          if (!holder) return;
          _data.root = [holder.orgRootId];
          if (_privilege.root && _privilege.child1 === null) {
          } else if (_privilege.child1 && _privilege.child2 === null) {
            _data.child1 = [holder.orgChild1Id];
          } else if (_privilege.child2 && _privilege.child3 === null) {
            _data.child1 = [holder.orgChild1Id];
            _data.child2 = [holder.orgChild2Id];
          } else if (_privilege.child3 && _privilege.child4 === null) {
            _data.child1 = [holder.orgChild1Id];
            _data.child2 = [holder.orgChild2Id];
            _data.child3 = [holder.orgChild3Id];
            _data.child4 = [holder.orgChild4Id];
          }
        } else {
          _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId];
        }
      } else {
        if (!attrOwnership) _data = _privilege;
      }
    }

    const orgRootData = await AppDataSource.getRepository(OrgRoot)
      .createQueryBuilder("orgRoot")
      .where("orgRoot.orgRevisionId = :id", { id })
      .andWhere(
        _data.root != undefined && _data.root != null
          ? _data.root[0] != null
            ? `orgRoot.id IN (:...node)`
            : `orgRoot.id is null`
          : "1=1",
        {
          node: _data.root,
        },
      )
      .select([
        "orgRoot.id",
        "orgRoot.ancestorDNA",
        "orgRoot.orgRootName",
        "orgRoot.orgRootShortName",
        "orgRoot.orgRootCode",
        "orgRoot.orgRootOrder",
        "orgRoot.orgRootPhoneEx",
        "orgRoot.orgRootPhoneIn",
        "orgRoot.orgRootFax",
        "orgRoot.orgRevisionId",
        "orgRoot.orgRootRank",
        "orgRoot.orgRootRankSub",
        "orgRoot.DEPARTMENT_CODE",
        "orgRoot.DIVISION_CODE",
        "orgRoot.SECTION_CODE",
        "orgRoot.JOB_CODE",
        "orgRoot.responsibility",
      ])
      .orderBy("orgRoot.orgRootOrder", "ASC")
      .getMany();
    const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null;
    const orgChild1Data =
      orgRootIds && orgRootIds.length > 0
        ? await AppDataSource.getRepository(OrgChild1)
            .createQueryBuilder("orgChild1")
            .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds })
            .andWhere(
              _data.child1 != undefined && _data.child1 != null
                ? _data.child1[0] != null
                  ? `orgChild1.id IN (:...node)`
                  : `orgChild1.id is ${_data.privilege == "PARENT" ? "not null" : "null"}`
                : "1=1",
              {
                node: _data.child1,
              },
            )
            .select([
              "orgChild1.id",
              "orgChild1.ancestorDNA",
              "orgChild1.orgChild1Name",
              "orgChild1.orgChild1ShortName",
              "orgChild1.orgChild1Code",
              "orgChild1.orgChild1Order",
              "orgChild1.orgChild1PhoneEx",
              "orgChild1.orgChild1PhoneIn",
              "orgChild1.orgChild1Fax",
              "orgChild1.orgRootId",
              "orgChild1.orgChild1Rank",
              "orgChild1.orgChild1RankSub",
              "orgChild1.DEPARTMENT_CODE",
              "orgChild1.DIVISION_CODE",
              "orgChild1.SECTION_CODE",
              "orgChild1.JOB_CODE",
              "orgChild1.responsibility",
            ])
            .orderBy("orgChild1.orgChild1Order", "ASC")
            .getMany()
        : [];

    const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null;
    const orgChild2Data =
      orgChild1Ids && orgChild1Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild2)
            .createQueryBuilder("orgChild2")
            .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids })
            .andWhere(
              _data.child2 != undefined && _data.child2 != null
                ? _data.child2[0] != null
                  ? `orgChild2.id IN (:...node)`
                  : `orgChild2.id is null`
                : "1=1",
              {
                node: _data.child2,
              },
            )
            .select([
              "orgChild2.id",
              "orgChild2.ancestorDNA",
              "orgChild2.orgChild2Name",
              "orgChild2.orgChild2ShortName",
              "orgChild2.orgChild2Code",
              "orgChild2.orgChild2Order",
              "orgChild2.orgChild2PhoneEx",
              "orgChild2.orgChild2PhoneIn",
              "orgChild2.orgChild2Fax",
              "orgChild2.orgRootId",
              "orgChild2.orgChild2Rank",
              "orgChild2.orgChild2RankSub",
              "orgChild2.DEPARTMENT_CODE",
              "orgChild2.DIVISION_CODE",
              "orgChild2.SECTION_CODE",
              "orgChild2.JOB_CODE",
              "orgChild2.orgChild1Id",
              "orgChild2.responsibility",
            ])
            .orderBy("orgChild2.orgChild2Order", "ASC")
            .getMany()
        : [];

    const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null;
    const orgChild3Data =
      orgChild2Ids && orgChild2Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild3)
            .createQueryBuilder("orgChild3")
            .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids })
            .andWhere(
              _data.child3 != undefined && _data.child3 != null
                ? _data.child3[0] != null
                  ? `orgChild3.id IN (:...node)`
                  : `orgChild3.id is null`
                : "1=1",
              {
                node: _data.child3,
              },
            )
            .select([
              "orgChild3.id",
              "orgChild3.ancestorDNA",
              "orgChild3.orgChild3Name",
              "orgChild3.orgChild3ShortName",
              "orgChild3.orgChild3Code",
              "orgChild3.orgChild3Order",
              "orgChild3.orgChild3PhoneEx",
              "orgChild3.orgChild3PhoneIn",
              "orgChild3.orgChild3Fax",
              "orgChild3.orgRootId",
              "orgChild3.orgChild3Rank",
              "orgChild3.orgChild3RankSub",
              "orgChild3.DEPARTMENT_CODE",
              "orgChild3.DIVISION_CODE",
              "orgChild3.SECTION_CODE",
              "orgChild3.JOB_CODE",
              "orgChild3.orgChild2Id",
              "orgChild3.responsibility",
            ])
            .orderBy("orgChild3.orgChild3Order", "ASC")
            .getMany()
        : [];

    const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null;
    const orgChild4Data =
      orgChild3Ids && orgChild3Ids.length > 0
        ? await AppDataSource.getRepository(OrgChild4)
            .createQueryBuilder("orgChild4")
            .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids })
            .andWhere(
              _data.child4 != undefined && _data.child4 != null
                ? _data.child4[0] != null
                  ? `orgChild4.id IN (:...node)`
                  : `orgChild4.id is null`
                : "1=1",
              {
                node: _data.child4,
              },
            )
            .select([
              "orgChild4.id",
              "orgChild4.ancestorDNA",
              "orgChild4.orgChild4Name",
              "orgChild4.orgChild4ShortName",
              "orgChild4.orgChild4Code",
              "orgChild4.orgChild4Order",
              "orgChild4.orgChild4PhoneEx",
              "orgChild4.orgChild4PhoneIn",
              "orgChild4.orgChild4Fax",
              "orgChild4.orgRootId",
              "orgChild4.orgChild4Rank",
              "orgChild4.orgChild4RankSub",
              "orgChild4.DEPARTMENT_CODE",
              "orgChild4.DIVISION_CODE",
              "orgChild4.SECTION_CODE",
              "orgChild4.JOB_CODE",
              "orgChild4.orgChild3Id",
              "orgChild4.responsibility",
            ])
            .orderBy("orgChild4.orgChild4Order", "ASC")
            .getMany()
        : [];

    // const formattedData = orgRootData.map((orgRoot) => {
    const formattedData = await Promise.all(
      orgRootData.map(async (orgRoot) => {
        // const sum0 = await this.posMasterRepository.find({
        //   where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id },
        //   select: [
        //     "current_holderId",
        //     "next_holderId",
        //     "orgRootId",
        //     "orgChild1Id",
        //     "orgChild2Id",
        //     "orgChild3Id",
        //     "orgChild4Id",
        //   ],
        // });
        return {
          orgTreeId: orgRoot.id,
          orgTreeDnaId: orgRoot.ancestorDNA,
          orgLevel: 0,
          orgName: orgRoot.orgRootName,
          orgTreeName: orgRoot.orgRootName,
          orgTreeShortName: orgRoot.orgRootShortName,
          orgTreeCode: orgRoot.orgRootCode,
          orgCode: orgRoot.orgRootCode + "00",
          orgTreeRank: orgRoot.orgRootRank,
          orgTreeRankSub: orgRoot.orgRootRankSub,
          DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE,
          DIVISION_CODE: orgRoot.DIVISION_CODE,
          SECTION_CODE: orgRoot.SECTION_CODE,
          JOB_CODE: orgRoot.JOB_CODE,
          orgTreeOrder: orgRoot.orgRootOrder,
          orgTreePhoneEx: orgRoot.orgRootPhoneEx,
          orgTreePhoneIn: orgRoot.orgRootPhoneIn,
          orgTreeFax: orgRoot.orgRootFax,
          orgRevisionId: orgRoot.orgRevisionId,
          orgRootName: orgRoot.orgRootName,
          responsibility: orgRoot.responsibility,
          labelName:
            orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName,
          // totalPosition: sum0.length + 1,
          // totalPositionCurrentUse:
          //   sum0.filter((x) => x.current_holderId != null && x.current_holderId != "").length + 1,
          // totalPositionCurrentVacant:
          //   sum0.filter((x) => x.current_holderId == null || x.current_holderId == "").length + 1,
          // totalPositionNextUse:
          //   sum0.filter((x) => x.next_holderId != null && x.next_holderId != "").length + 1,
          // totalPositionNextVacant:
          //   sum0.filter((x) => x.next_holderId == null || x.next_holderId == "").length + 1,
          // totalRootPosition:
          //   sum0.filter(
          //     (x) =>
          //       (x.orgChild1Id == null || x.orgChild1Id == "") &&
          //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
          //       (x.orgChild4Id == null || x.orgChild4Id == ""),
          //   ).length + 1,
          // totalRootPositionCurrentUse:
          //   sum0.filter(
          //     (x) =>
          //       (x.orgChild1Id == null || x.orgChild1Id == "") &&
          //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
          //       x.current_holderId != null &&
          //       x.current_holderId != "",
          //   ).length + 1,
          // totalRootPositionCurrentVacant:
          //   sum0.filter(
          //     (x) =>
          //       (x.orgChild1Id == null || x.orgChild1Id == "") &&
          //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
          //       (x.current_holderId == null || x.current_holderId == ""),
          //   ).length + 1,
          // totalRootPositionNextUse:
          //   sum0.filter(
          //     (x) =>
          //       (x.orgChild1Id == null || x.orgChild1Id == "") &&
          //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
          //       x.next_holderId != null &&
          //       x.next_holderId != "",
          //   ).length + 1,
          // totalRootPositionNextVacant:
          //   sum0.filter(
          //     (x) =>
          //       (x.orgChild1Id == null || x.orgChild1Id == "") &&
          //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
          //       (x.next_holderId == null || x.next_holderId == ""),
          //   ).length + 1,

          children: await Promise.all(
            orgChild1Data
              .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id)
              .map(async (orgChild1) => {
                // const sum1 = await this.posMasterRepository.find({
                //   where: {
                //     orgRevisionId: orgRoot.orgRevisionId,
                //     orgRootId: orgRoot.id,
                //     orgChild1Id: orgChild1.id,
                //   },
                //   select: [
                //     "current_holderId",
                //     "next_holderId",
                //     "orgRootId",
                //     "orgChild1Id",
                //     "orgChild2Id",
                //     "orgChild3Id",
                //     "orgChild4Id",
                //   ],
                // });
                return {
                  orgTreeId: orgChild1.id,
                  orgTreeDnaId: orgChild1.ancestorDNA,
                  orgRootId: orgRoot.id,
                  orgRootDnaId: orgRoot.ancestorDNA,
                  orgLevel: 1,
                  orgName: `${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`,
                  orgTreeName: orgChild1.orgChild1Name,
                  orgTreeShortName: orgChild1.orgChild1ShortName,
                  orgTreeCode: orgChild1.orgChild1Code,
                  orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code,
                  orgTreeRank: orgChild1.orgChild1Rank,
                  orgTreeRankSub: orgChild1.orgChild1RankSub,
                  DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE,
                  DIVISION_CODE: orgChild1.DIVISION_CODE,
                  SECTION_CODE: orgChild1.SECTION_CODE,
                  JOB_CODE: orgChild1.JOB_CODE,
                  orgTreeOrder: orgChild1.orgChild1Order,
                  orgRootCode: orgRoot.orgRootCode,
                  orgTreePhoneEx: orgChild1.orgChild1PhoneEx,
                  orgTreePhoneIn: orgChild1.orgChild1PhoneIn,
                  orgTreeFax: orgChild1.orgChild1Fax,
                  orgRevisionId: orgRoot.orgRevisionId,
                  orgRootName: orgRoot.orgRootName,
                  responsibility: orgChild1.responsibility,
                  labelName:
                    orgChild1.orgChild1Name +
                    " " +
                    orgRoot.orgRootCode +
                    orgChild1.orgChild1Code +
                    " " +
                    orgChild1.orgChild1ShortName +
                    "/" +
                    orgRoot.orgRootName +
                    " " +
                    orgRoot.orgRootCode +
                    "00" +
                    " " +
                    orgRoot.orgRootShortName,
                  // totalPosition: sum1.length + 1,
                  // totalPositionCurrentUse:
                  //   sum1.filter((x) => x.current_holderId != null && x.current_holderId != "")
                  //     .length + 1,
                  // totalPositionCurrentVacant:
                  //   sum1.filter((x) => x.current_holderId == null || x.current_holderId == "")
                  //     .length + 1,
                  // totalPositionNextUse:
                  //   sum1.filter((x) => x.next_holderId != null && x.next_holderId != "").length + 1,
                  // totalPositionNextVacant:
                  //   sum1.filter((x) => x.next_holderId == null || x.next_holderId == "").length + 1,
                  // totalRootPosition:
                  //   sum1.filter(
                  //     (x) =>
                  //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
                  //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                  //       (x.orgChild4Id == null || x.orgChild4Id == ""),
                  //   ).length + 1,
                  // totalRootPositionCurrentUse:
                  //   sum1.filter(
                  //     (x) =>
                  //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
                  //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                  //       x.current_holderId != null &&
                  //       x.current_holderId != "",
                  //   ).length + 1,
                  // totalRootPositionCurrentVacant:
                  //   sum1.filter(
                  //     (x) =>
                  //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
                  //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                  //       (x.current_holderId == null || x.current_holderId == ""),
                  //   ).length + 1,
                  // totalRootPositionNextUse:
                  //   sum1.filter(
                  //     (x) =>
                  //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
                  //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                  //       x.next_holderId != null &&
                  //       x.next_holderId != "",
                  //   ).length + 1,
                  // totalRootPositionNextVacant:
                  //   sum1.filter(
                  //     (x) =>
                  //       (x.orgChild2Id == null || x.orgChild2Id == "") &&
                  //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                  //       (x.next_holderId == null || x.next_holderId == ""),
                  //   ).length + 1,

                  children: await Promise.all(
                    orgChild2Data
                      .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id)
                      .map(async (orgChild2) => {
                        // const sum2 = await this.posMasterRepository.find({
                        //   where: {
                        //     orgRevisionId: orgRoot.orgRevisionId,
                        //     orgRootId: orgRoot.id,
                        //     orgChild1Id: orgChild1.id,
                        //     orgChild2Id: orgChild2.id,
                        //   },
                        //   select: [
                        //     "current_holderId",
                        //     "next_holderId",
                        //     "orgRootId",
                        //     "orgChild1Id",
                        //     "orgChild2Id",
                        //     "orgChild3Id",
                        //     "orgChild4Id",
                        //   ],
                        // });
                        return {
                          orgTreeId: orgChild2.id,
                          orgTreeDnaId: orgChild2.ancestorDNA,
                          orgRootId: orgChild1.id,
                          orgRootDnaId: orgChild1.ancestorDNA,
                          orgLevel: 2,
                          orgName: `${orgChild2.orgChild2Name} ${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`,
                          orgTreeName: orgChild2.orgChild2Name,
                          orgTreeShortName: orgChild2.orgChild2ShortName,
                          orgTreeCode: orgChild2.orgChild2Code,
                          orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code,
                          orgTreeRank: orgChild2.orgChild2Rank,
                          orgTreeRankSub: orgChild2.orgChild2RankSub,
                          DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE,
                          DIVISION_CODE: orgChild2.DIVISION_CODE,
                          SECTION_CODE: orgChild2.SECTION_CODE,
                          JOB_CODE: orgChild2.JOB_CODE,
                          orgTreeOrder: orgChild2.orgChild2Order,
                          orgRootCode: orgRoot.orgRootCode,
                          orgTreePhoneEx: orgChild2.orgChild2PhoneEx,
                          orgTreePhoneIn: orgChild2.orgChild2PhoneIn,
                          orgTreeFax: orgChild2.orgChild2Fax,
                          orgRevisionId: orgRoot.orgRevisionId,
                          orgRootName: orgRoot.orgRootName,
                          responsibility: orgChild2.responsibility,
                          labelName:
                            orgChild2.orgChild2Name +
                            " " +
                            orgRoot.orgRootCode +
                            orgChild2.orgChild2Code +
                            " " +
                            orgChild2.orgChild2ShortName +
                            "/" +
                            orgChild1.orgChild1Name +
                            " " +
                            orgRoot.orgRootCode +
                            orgChild1.orgChild1Code +
                            " " +
                            orgChild1.orgChild1ShortName +
                            "/" +
                            orgRoot.orgRootName +
                            " " +
                            orgRoot.orgRootCode +
                            "00" +
                            " " +
                            orgRoot.orgRootShortName,
                          // totalPosition: sum2.length + 1,
                          // totalPositionCurrentUse:
                          //   sum2.filter(
                          //     (x) => x.current_holderId != null && x.current_holderId != "",
                          //   ).length + 1,
                          // totalPositionCurrentVacant:
                          //   sum2.filter(
                          //     (x) => x.current_holderId == null || x.current_holderId == "",
                          //   ).length + 1,
                          // totalPositionNextUse:
                          //   sum2.filter((x) => x.next_holderId != null && x.next_holderId != "")
                          //     .length + 1,
                          // totalPositionNextVacant:
                          //   sum2.filter((x) => x.next_holderId == null || x.next_holderId == "")
                          //     .length + 1,
                          // totalRootPosition:
                          //   sum2.filter(
                          //     (x) =>
                          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                          //       (x.orgChild4Id == null || x.orgChild4Id == ""),
                          //   ).length + 1,
                          // totalRootPositionCurrentUse:
                          //   sum2.filter(
                          //     (x) =>
                          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                          //       x.current_holderId != null &&
                          //       x.current_holderId != "",
                          //   ).length + 1,
                          // totalRootPositionCurrentVacant:
                          //   sum2.filter(
                          //     (x) =>
                          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                          //       (x.current_holderId == null || x.current_holderId == ""),
                          //   ).length + 1,
                          // totalRootPositionNextUse:
                          //   sum2.filter(
                          //     (x) =>
                          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                          //       x.next_holderId != null &&
                          //       x.next_holderId != "",
                          //   ).length + 1,
                          // totalRootPositionNextVacant:
                          //   sum2.filter(
                          //     (x) =>
                          //       (x.orgChild3Id == null || x.orgChild3Id == "") &&
                          //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                          //       (x.next_holderId == null || x.next_holderId == ""),
                          //   ).length + 1,

                          children: await Promise.all(
                            orgChild3Data
                              .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id)
                              .map(async (orgChild3) => {
                                // const sum3 = await this.posMasterRepository.find({
                                //   where: {
                                //     orgRevisionId: orgRoot.orgRevisionId,
                                //     orgRootId: orgRoot.id,
                                //     orgChild1Id: orgChild1.id,
                                //     orgChild2Id: orgChild2.id,
                                //     orgChild3Id: orgChild3.id,
                                //   },
                                //   select: [
                                //     "current_holderId",
                                //     "next_holderId",
                                //     "orgRootId",
                                //     "orgChild1Id",
                                //     "orgChild2Id",
                                //     "orgChild3Id",
                                //     "orgChild4Id",
                                //   ],
                                // });
                                return {
                                  orgTreeId: orgChild3.id,
                                  orgTreeDnaId: orgChild3.ancestorDNA,
                                  orgRootId: orgChild2.id,
                                  orgRootDnaId: orgChild2.ancestorDNA,
                                  orgLevel: 3,
                                  orgName: `${orgChild3.orgChild3Name} ${orgChild2.orgChild2Name} ${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`,
                                  orgTreeName: orgChild3.orgChild3Name,
                                  orgTreeShortName: orgChild3.orgChild3ShortName,
                                  orgTreeCode: orgChild3.orgChild3Code,
                                  orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code,
                                  orgTreeRank: orgChild3.orgChild3Rank,
                                  orgTreeRankSub: orgChild3.orgChild3RankSub,
                                  DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE,
                                  DIVISION_CODE: orgChild3.DIVISION_CODE,
                                  SECTION_CODE: orgChild3.SECTION_CODE,
                                  JOB_CODE: orgChild3.JOB_CODE,
                                  orgTreeOrder: orgChild3.orgChild3Order,
                                  orgRootCode: orgRoot.orgRootCode,
                                  orgTreePhoneEx: orgChild3.orgChild3PhoneEx,
                                  orgTreePhoneIn: orgChild3.orgChild3PhoneIn,
                                  orgTreeFax: orgChild3.orgChild3Fax,
                                  orgRevisionId: orgRoot.orgRevisionId,
                                  orgRootName: orgRoot.orgRootName,
                                  responsibility: orgChild3.responsibility,
                                  labelName:
                                    orgChild3.orgChild3Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild3.orgChild3Code +
                                    " " +
                                    orgChild3.orgChild3ShortName +
                                    "/" +
                                    orgChild2.orgChild2Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild2.orgChild2Code +
                                    " " +
                                    orgChild2.orgChild2ShortName +
                                    "/" +
                                    orgChild1.orgChild1Name +
                                    " " +
                                    orgRoot.orgRootCode +
                                    orgChild1.orgChild1Code +
                                    " " +
                                    orgChild1.orgChild1ShortName +
                                    "/" +
                                    orgRoot.orgRootName +
                                    " " +
                                    orgRoot.orgRootCode +
                                    "00" +
                                    " " +
                                    orgRoot.orgRootShortName,
                                  // totalPosition: sum3.length + 1,
                                  // totalPositionCurrentUse:
                                  //   sum3.filter(
                                  //     (x) => x.current_holderId != null && x.current_holderId != "",
                                  //   ).length + 1,
                                  // totalPositionCurrentVacant:
                                  //   sum3.filter(
                                  //     (x) => x.current_holderId == null || x.current_holderId == "",
                                  //   ).length + 1,
                                  // totalPositionNextUse:
                                  //   sum3.filter(
                                  //     (x) => x.next_holderId != null && x.next_holderId != "",
                                  //   ).length + 1,
                                  // totalPositionNextVacant:
                                  //   sum3.filter(
                                  //     (x) => x.next_holderId == null || x.next_holderId == "",
                                  //   ).length + 1,
                                  // totalRootPosition:
                                  //   sum3.filter((x) => x.orgChild4Id == null || x.orgChild4Id == "")
                                  //     .length + 1,
                                  // totalRootPositionCurrentUse:
                                  //   sum3.filter(
                                  //     (x) =>
                                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                                  //       x.current_holderId != null &&
                                  //       x.current_holderId != "",
                                  //   ).length + 1,
                                  // totalRootPositionCurrentVacant:
                                  //   sum3.filter(
                                  //     (x) =>
                                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                                  //       (x.current_holderId == null || x.current_holderId == ""),
                                  //   ).length + 1,
                                  // totalRootPositionNextUse:
                                  //   sum3.filter(
                                  //     (x) =>
                                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                                  //       x.next_holderId != null &&
                                  //       x.next_holderId != "",
                                  //   ).length + 1,
                                  // totalRootPositionNextVacant:
                                  //   sum3.filter(
                                  //     (x) =>
                                  //       (x.orgChild4Id == null || x.orgChild4Id == "") &&
                                  //       (x.next_holderId == null || x.next_holderId == ""),
                                  //   ).length + 1,

                                  children: await Promise.all(
                                    orgChild4Data
                                      .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id)
                                      .map(async (orgChild4) => {
                                        // const sum4 = await this.posMasterRepository.find({
                                        //   where: {
                                        //     orgRevisionId: orgRoot.orgRevisionId,
                                        //     orgRootId: orgRoot.id,
                                        //     orgChild1Id: orgChild1.id,
                                        //     orgChild2Id: orgChild2.id,
                                        //     orgChild3Id: orgChild3.id,
                                        //     orgChild4Id: orgChild4.id,
                                        //   },
                                        //   select: [
                                        //     "current_holderId",
                                        //     "next_holderId",
                                        //     "orgRootId",
                                        //     "orgChild1Id",
                                        //     "orgChild2Id",
                                        //     "orgChild3Id",
                                        //     "orgChild4Id",
                                        //   ],
                                        // });
                                        return {
                                          orgTreeId: orgChild4.id,
                                          orgTreeDnaId: orgChild4.ancestorDNA,
                                          orgRootId: orgChild3.id,
                                          orgRootDnaId: orgChild3.ancestorDNA,
                                          orgLevel: 4,
                                          orgName: `${orgChild4.orgChild4Name} ${orgChild3.orgChild3Name} ${orgChild2.orgChild2Name} ${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`,
                                          orgTreeName: orgChild4.orgChild4Name,
                                          orgTreeShortName: orgChild4.orgChild4ShortName,
                                          orgTreeCode: orgChild4.orgChild4Code,
                                          orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code,
                                          orgTreeRank: orgChild4.orgChild4Rank,
                                          orgTreeRankSub: orgChild4.orgChild4RankSub,
                                          DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE,
                                          DIVISION_CODE: orgChild4.DIVISION_CODE,
                                          SECTION_CODE: orgChild4.SECTION_CODE,
                                          JOB_CODE: orgChild4.JOB_CODE,
                                          orgTreeOrder: orgChild4.orgChild4Order,
                                          orgRootCode: orgRoot.orgRootCode,
                                          orgTreePhoneEx: orgChild4.orgChild4PhoneEx,
                                          orgTreePhoneIn: orgChild4.orgChild4PhoneIn,
                                          orgTreeFax: orgChild4.orgChild4Fax,
                                          orgRevisionId: orgRoot.orgRevisionId,
                                          orgRootName: orgRoot.orgRootName,
                                          responsibility: orgChild4.responsibility,
                                          labelName:
                                            orgChild4.orgChild4Name +
                                            " " +
                                            orgRoot.orgRootCode +
                                            orgChild4.orgChild4Code +
                                            " " +
                                            orgChild4.orgChild4ShortName +
                                            "/" +
                                            orgChild3.orgChild3Name +
                                            " " +
                                            orgRoot.orgRootCode +
                                            orgChild3.orgChild3Code +
                                            " " +
                                            orgChild3.orgChild3ShortName +
                                            "/" +
                                            orgChild2.orgChild2Name +
                                            " " +
                                            orgRoot.orgRootCode +
                                            orgChild2.orgChild2Code +
                                            " " +
                                            orgChild2.orgChild2ShortName +
                                            "/" +
                                            orgChild1.orgChild1Name +
                                            " " +
                                            orgRoot.orgRootCode +
                                            orgChild1.orgChild1Code +
                                            " " +
                                            orgChild1.orgChild1ShortName +
                                            "/" +
                                            orgRoot.orgRootName +
                                            " " +
                                            orgRoot.orgRootCode +
                                            "00" +
                                            " " +
                                            orgRoot.orgRootShortName,
                                          // totalPosition: sum4.length + 1,
                                          // totalPositionCurrentUse:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.current_holderId != null &&
                                          //       x.current_holderId != "",
                                          //   ).length + 1,
                                          // totalPositionCurrentVacant:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.current_holderId == null ||
                                          //       x.current_holderId == "",
                                          //   ).length + 1,
                                          // totalPositionNextUse:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.next_holderId != null && x.next_holderId != "",
                                          //   ).length + 1,
                                          // totalPositionNextVacant:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.next_holderId == null || x.next_holderId == "",
                                          //   ).length + 1,
                                          // totalRootPosition: sum4.length + 1,
                                          // totalRootPositionCurrentUse:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.current_holderId != null &&
                                          //       x.current_holderId != "",
                                          //   ).length + 1,
                                          // totalRootPositionCurrentVacant:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.current_holderId == null ||
                                          //       x.current_holderId == "",
                                          //   ).length + 1,
                                          // totalRootPositionNextUse:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.next_holderId != null && x.next_holderId != "",
                                          //   ).length + 1,
                                          // totalRootPositionNextVacant:
                                          //   sum4.filter(
                                          //     (x) =>
                                          //       x.next_holderId == null || x.next_holderId == "",
                                          //   ).length + 1,
                                        };
                                      }),
                                  ),
                                };
                              }),
                          ),
                        };
                      }),
                  ),
                };
              }),
          ),
        };
      }),
    );

    return new HttpSuccess(formattedData);
  }
  /**
   * API รายละเอียดโครงสร้าง
   *
   * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25
   *
   */
  @Get("system-root/{id}/{system}")
  async detailBySystemRoot(
    @Path() id: string,
    @Path() system: string,
    @Request() request: RequestWithUser,
  ) {
    const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }
    let _data = {
      root: null,
      child1: null,
      child2: null,
      child3: null,
      child4: null,
    };
    if (orgRevision.orgRevisionIsDraft == true && orgRevision.orgRevisionIsCurrent == false) {
      _data = await new permission().PermissionOrgList(request, system.trim().toUpperCase());
    }
    const orgRootData = await AppDataSource.getRepository(OrgRoot)
      .createQueryBuilder("orgRoot")
      .where("orgRoot.orgRevisionId = :id", { id })
      .andWhere(
        _data.root != undefined && _data.root != null
          ? _data.root[0] != null
            ? `orgRoot.id IN (:...node)`
            : `orgRoot.id is null`
          : "1=1",
        {
          node: _data.root,
        },
      )
      .select([
        "orgRoot.id",
        "orgRoot.orgRootName",
        "orgRoot.orgRootShortName",
        "orgRoot.orgRootCode",
        "orgRoot.orgRootOrder",
        "orgRoot.orgRootPhoneEx",
        "orgRoot.orgRootPhoneIn",
        "orgRoot.orgRootFax",
        "orgRoot.orgRevisionId",
        "orgRoot.orgRootRank",
        "orgRoot.orgRootRankSub",
        "orgRoot.responsibility",
        "orgRoot.isDeputy",
      ])
      .orderBy("orgRoot.orgRootOrder", "ASC")
      .getMany();

    const formattedData = await Promise.all(
      orgRootData.map(async (orgRoot) => {
        return {
          orgTreeId: orgRoot.id,
          orgLevel: 0,
          isDeputy: orgRoot.isDeputy,
          orgName: orgRoot.orgRootName,
          orgTreeName: orgRoot.orgRootName,
          orgTreeShortName: orgRoot.orgRootShortName,
          orgTreeCode: orgRoot.orgRootCode,
          orgCode: orgRoot.orgRootCode + "00",
          orgTreeRank: orgRoot.orgRootRank,
          orgTreeRankSub: orgRoot.orgRootRankSub,
          orgTreeOrder: orgRoot.orgRootOrder,
          orgTreePhoneEx: orgRoot.orgRootPhoneEx,
          orgTreePhoneIn: orgRoot.orgRootPhoneIn,
          orgTreeFax: orgRoot.orgRootFax,
          orgRevisionId: orgRoot.orgRevisionId,
          orgRootName: orgRoot.orgRootName,
          responsibility: orgRoot.responsibility,
          labelName:
            orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName,
          totalPosition: await this.posMasterRepository.count({
            where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id },
          }),
          totalPositionCurrentUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              current_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalPositionCurrentVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              current_holderId: IsNull() || "",
            },
          }),
          totalPositionNextUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              next_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalPositionNextVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              next_holderId: IsNull() || "",
            },
          }),
          totalRootPosition: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
            },
          }),
          totalRootPositionCurrentUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              current_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalRootPositionCurrentVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              current_holderId: IsNull() || "",
            },
          }),
          totalRootPositionNextUse: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              next_holderId: Not(IsNull()) || Not(""),
            },
          }),
          totalRootPositionNextVacant: await this.posMasterRepository.count({
            where: {
              orgRevisionId: orgRoot.orgRevisionId,
              orgRootId: orgRoot.id,
              orgChild1Id: IsNull() || "",
              orgChild2Id: IsNull() || "",
              orgChild3Id: IsNull() || "",
              orgChild4Id: IsNull() || "",
              next_holderId: IsNull() || "",
            },
          }),
        };
      }),
    );

    return new HttpSuccess(formattedData);
  }
  /**
   * API เช็ค org ในระบบ
   *
   * @summary - เช็ค org ในระบบ (ADMIN)
   *
   */
  @Get("check/child1/{id}")
  async findIsOfficerChild1(@Path() id: string, @Request() request: RequestWithUser) {
    const orgRevision = await this.orgRevisionRepository.findOne({
      where: { id },
      relations: ["orgChild1s"],
    });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }
    const check = orgRevision.orgChild1s.find((x) => x.isOfficer == true);
    return new HttpSuccess(check != null);
  }
  /**
   * API เช็ค org ในระบบ
   *
   * @summary - เช็ค org ในระบบ (ADMIN)
   *
   */
  @Get("check/root/{id}")
  async findIsDeputyRoot(@Path() id: string, @Request() request: RequestWithUser) {
    const orgRevision = await this.orgRevisionRepository.findOne({
      where: { id },
      relations: ["orgRoots"],
    });
    if (!orgRevision) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }
    const check = orgRevision.orgRoots.find((x) => x.isDeputy == true);
    return new HttpSuccess(check != null);
  }

  /**
   * API หา สกก1
   *
   * @summary - หา สกก1 (ADMIN)
   *
   */
  @Get("find/head/officer")
  async findIsHeadOfficer(@Request() request: RequestWithUser) {
    const posMaster = await this.posMasterRepository.find({
      where: {
        orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
        orgRoot: { isCommission: true },
        orgChild1Id: IsNull(),
        isDirector: true,
        current_holderId: Not(IsNull()),
        // posMasterActs: { statusReport: "DONE" },
        // posMasterActChilds: { statusReport: "DONE" },
      },
      order: { posMasterOrder: "ASC", posMasterActChilds: { posMasterOrder: "ASC" } },
      relations: [
        "current_holder",
        // "posMasterActChilds",
        // "posMasterActChilds.posMasterChild",
        // "posMasterActChilds.posMasterChild.current_holder",
      ],
    });
    if (posMaster.length <= 0) {
      return new HttpSuccess({
        name: "......................................................",
        position: "......................................................",
      });
    }
    if (posMaster[0].current_holder == null) {
      const posMaster = await this.posMasterRepository.find({
        where: {
          orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
          orgRoot: { isCommission: true },
          orgChild1Id: IsNull(),
          isDirector: true,
          posMasterActs: { statusReport: "DONE" },
          posMasterActChilds: { statusReport: "DONE" },
        },
        order: { posMasterOrder: "ASC", posMasterActChilds: { posMasterOrder: "ASC" } },
        relations: [
          "current_holder",
          "posMasterActChilds",
          "posMasterActChilds.posMasterChild",
          "posMasterActChilds.posMasterChild.current_holder",
        ],
      });
      if (
        posMaster[0].posMasterActChilds.length <= 0 ||
        posMaster[0].posMasterActChilds[0].posMasterChild == null ||
        posMaster[0].posMasterActChilds[0].posMasterChild.current_holder == null
      ) {
        return new HttpSuccess({
          name: "......................................................",
          position: "......................................................",
        });
      }
      return new HttpSuccess({
        name: `${posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.prefix}${posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.firstName} ${posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.lastName}`,
        position: posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.position,
      });
    } else {
      return new HttpSuccess({
        name: `${posMaster[0].current_holder.prefix}${posMaster[0].current_holder.firstName} ${posMaster[0].current_holder.lastName}`,
        position: posMaster[0].current_holder.position,
      });
    }
  }

  /**
   * API ลำดับโครงสร้าง
   *
   * @summary - ลำดับโครงสร้าง (ADMIN)
   *
   */
  @Get("root/search/sort")
  async searchSortRootLevelType(@Request() request: RequestWithUser) {
    // const root1 = await this.orgRootRepository.find({
    //   where: {
    //     orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //     DEPARTMENT_CODE: Not("50"),
    //   },
    //   order: { isDeputy: "DESC", orgRootOrder: "ASC" },
    //   select: ["orgRootName"],
    // });
    // const root2 = await this.orgRootRepository.find({
    //   where: {
    //     orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //     DEPARTMENT_CODE: "50",
    //   },
    //   order: { orgRootName: "ASC" },
    //   select: ["orgRootName"],
    // });
    // const root = [...root1, ...root2];

    // const child1 = await this.child1Repository.find({
    //   where: {
    //     orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //   },
    //   order: { orgChild1Order: "ASC" },
    //   select: ["orgChild1Name"],
    // });
    // const child2 = await this.child2Repository.find({
    //   where: {
    //     orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //   },
    //   order: { orgChild2Order: "ASC" },
    //   select: ["orgChild2Name"],
    // });
    // const child3 = await this.child3Repository.find({
    //   where: {
    //     orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //   },
    //   order: { orgChild3Order: "ASC" },
    //   select: ["orgChild3Name"],
    // });
    // const child4 = await this.child4Repository.find({
    //   where: {
    //     orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //   },
    //   order: { orgChild4Order: "ASC" },
    //   select: ["orgChild4Name"],
    // });
    // const hospital = await this.child1Repository.find({
    //   where: [
    //     {
    //       orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //       orgRoot: { isDeputy: true },
    //     },
    //     {
    //       orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true },
    //       orgChild1RankSub: "HOSPITAL",
    //     },
    //   ],
    //   select: ["orgChild1Name"],
    // });
    // const posType = await this.posTypeRepository.find({
    //   order: { posTypeRank: "DESC" },
    //   select: ["posTypeName"],
    // });
    // const posLevel = await this.posLevelRepository.find({
    //   order: { posLevelRank: "DESC" },
    //   select: ["posLevelName"],
    // });

    const [roots, child1, child2, child3, child4, hospital, posType, posLevel] = await Promise.all([
      // ===== ROOT =====
      this.orgRootRepository
        .createQueryBuilder("root")
        .innerJoin("root.orgRevision", "rev")
        .select(["root.orgRootName AS orgRootName"])
        .where("rev.orgRevisionIsDraft = false")
        .andWhere("rev.orgRevisionIsCurrent = true")
        .orderBy("CASE WHEN root.DEPARTMENT_CODE = '50' THEN 1 ELSE 0 END", "ASC")
        .addOrderBy("root.isDeputy", "DESC")
        .addOrderBy("root.orgRootOrder", "ASC")
        .addOrderBy("root.orgRootName", "ASC")
        .getRawMany(),

      // ===== CHILD 1 =====
      this.child1Repository
        .createQueryBuilder("c1")
        .innerJoin("c1.orgRevision", "rev")
        .select("c1.orgChild1Name", "orgChild1Name")
        .where("rev.orgRevisionIsDraft = false")
        .andWhere("rev.orgRevisionIsCurrent = true")
        .orderBy("c1.orgChild1Order", "ASC")
        .getRawMany(),

      // ===== CHILD 2 =====
      this.child2Repository
        .createQueryBuilder("c2")
        .innerJoin("c2.orgRevision", "rev")
        .select("c2.orgChild2Name", "orgChild2Name")
        .where("rev.orgRevisionIsDraft = false")
        .andWhere("rev.orgRevisionIsCurrent = true")
        .orderBy("c2.orgChild2Order", "ASC")
        .getRawMany(),

      // ===== CHILD 3 =====
      this.child3Repository
        .createQueryBuilder("c3")
        .innerJoin("c3.orgRevision", "rev")
        .select("c3.orgChild3Name", "orgChild3Name")
        .where("rev.orgRevisionIsDraft = false")
        .andWhere("rev.orgRevisionIsCurrent = true")
        .orderBy("c3.orgChild3Order", "ASC")
        .getRawMany(),

      // ===== CHILD 4 =====
      this.child4Repository
        .createQueryBuilder("c4")
        .innerJoin("c4.orgRevision", "rev")
        .select("c4.orgChild4Name", "orgChild4Name")
        .where("rev.orgRevisionIsDraft = false")
        .andWhere("rev.orgRevisionIsCurrent = true")
        .orderBy("c4.orgChild4Order", "ASC")
        .getRawMany(),

      // ===== HOSPITAL =====
      this.child1Repository
        .createQueryBuilder("c1")
        .innerJoin("c1.orgRevision", "rev")
        .leftJoin("c1.orgRoot", "root")
        .select("c1.orgChild1Name", "orgChild1Name")
        .where("rev.orgRevisionIsDraft = false")
        .andWhere("rev.orgRevisionIsCurrent = true")
        .andWhere("(root.isDeputy = true OR c1.orgChild1RankSub = :rank)", { rank: "HOSPITAL" })
        .getRawMany(),

      // ===== POSITION TYPE =====
      this.posTypeRepository
        .createQueryBuilder("pt")
        .select("pt.posTypeName", "posTypeName")
        .orderBy("pt.posTypeRank", "DESC")
        .getRawMany(),

      // ===== POSITION LEVEL =====
      this.posLevelRepository
        .createQueryBuilder("pl")
        .select("pl.posLevelName", "posLevelName")
        .orderBy("pl.posLevelRank", "DESC")
        .getRawMany(),
    ]);

    return new HttpSuccess({
      root: roots.map((x) => x.orgRootName),
      child1: child1.map((x) => x.orgChild1Name),
      child2: child2.map((x) => x.orgChild2Name),
      child3: child3.map((x) => x.orgChild3Name),
      child4: child4.map((x) => x.orgChild4Name),
      hospital: hospital.map((x) => x.orgChild1Name),
      posTypeNameOrder: posType.map((x) => x.posTypeName),
      posLevelNameOrder: posLevel.map((x) => x.posLevelName),
    });
  }

  /**
   * API เพิ่มสิทธิ์โครงสร้าง
   *
   * @summary - เพิ่มสิทธิ์โครงสร้าง (ADMIN)
   *
   */
  @Get("root/add/permission/{child1Id}")
  async addRootPermission(@Path() child1Id: string, @Request() request: RequestWithUser) {
    const profiles = await this.profileRepo.find({
      where: {
        keycloak: Not(IsNull()),
        current_holders: {
          orgChild1Id: child1Id,
        },
      },
    });
    const orgRoots = await this.orgRootRepository.find({
      where: {
        orgRevision: {
          orgRevisionIsDraft: true,
          orgRevisionIsCurrent: false,
        },
      },
    });

    for await (const root of orgRoots) {
      const _permissionOrg = profiles.map((profile) => {
        const permission = new PermissionOrg();
        permission.orgRootId = root.id;
        permission.profileId = profile.id;
        permission.createdUserId = request.user.sub;
        permission.createdFullName = request.user.name;
        permission.lastUpdateUserId = request.user.sub;
        permission.lastUpdateFullName = request.user.name;
        permission.createdAt = new Date();
        permission.lastUpdatedAt = new Date();
        return permission;
      });
      await this.permissionOrgRepository.save(_permissionOrg);
    }

    return new HttpSuccess();
  }

  /**
   * API ลบข้าราชการในโครงสร้าง
   *
   * @summary - ลบข้าราชการในโครงสร้าง (ADMIN)
   *
   */
  @Get("delete/profile-officer/org/{orgRevisionId}")
  async deleteOfficerRetireInOrg(
    @Path() orgRevisionId: string,
    @Request() request: RequestWithUser,
  ) {
    const posMasters = await this.posMasterRepository.find({
      where: {
        orgRevisionId,
        current_holderId: Not(IsNull()),
        current_holder: {
          isLeave: true,
          isRetirement: true,
        },
        // positions: { positionIsSelected: true },
      },
      relations: ["positions"],
    });

    let checkOfficer = 0;

    await Promise.all([
      posMasters.map(async (posMaster) => {
        posMaster.current_holderId = null;
        posMaster.isSit = false;
        if (posMaster.positions) {
          for (const position of posMaster.positions) {
            position.positionIsSelected = false;
            await this.positionRepository.save(position);
            checkOfficer += 1;
          }
        }
        await this.posMasterRepository.save(posMaster);
        await CreatePosMasterHistoryOfficer(posMaster.id, null);
      }),
    ]);

    return new HttpSuccess({
      totalOfficer: posMasters.length,
      officerSuccessAmount: checkOfficer,
    });
  }

  /**
   * API ลบลูกจ้างในโครงสร้าง
   *
   * @summary - ลบลูกจ้างในโครงสร้าง (ADMIN)
   *
   */
  @Get("delete/profile-emp/org/{orgRevisionId}")
  async deleteRetireEmpInOrg(@Path() orgRevisionId: string, @Request() request: RequestWithUser) {
    const posMastersEmployee = await this.employeePosMasterRepository.find({
      where: {
        orgRevisionId,
        current_holderId: Not(IsNull()),
        current_holder: {
          isLeave: true,
          isRetirement: true,
        },
        // positions: { positionIsSelected: true },
      },
      relations: ["positions"],
    });

    let checkEmployee = 0;

    await Promise.all(
      posMastersEmployee.map(async (posMaster) => {
        posMaster.current_holderId = null;
        posMaster.isSit = false;
        if (posMaster.positions) {
          for (const position of posMaster.positions) {
            position.positionIsSelected = false;
            await this.employeePositionRepository.save(position);
            checkEmployee += 1;
          }
        }
        await this.employeePosMasterRepository.save(posMaster);
        await CreatePosMasterHistoryEmployee(posMaster.id, null);
      }),
    );

    return new HttpSuccess({
      totalEmployee: posMastersEmployee.length,
      employeeSuccessAmount: checkEmployee,
    });
  }

  /**
   * API บันทึกลงประวัติตำแหน่ง
   *
   * @summary - บันทึกลงประวัติตำแหน่ง (ADMIN)
   *
   */
  @Get("save/profile/position-history")
  async saveRetireToPositionHistory(@Request() request: RequestWithUser) {
    const [profileLeave, profileEmployeeLeave] = await Promise.all([
      this.profileRepo.find({
        where: {
          isLeave: true,
          isRetirement: true,
          leaveType: IsNull(),
        },
      }),
      this.profileEmployeeRepo.find({
        where: {
          isLeave: true,
          isRetirement: true,
          leaveType: IsNull(),
        },
      }),
    ]);

    let checkOfficer: number = 0;
    await Promise.all(
      profileLeave.map(async (profile: any) => {
        const dest_item = await this.profileSalaryRepository.findOne({
          where: { profileId: profile.id },
          order: { order: "DESC" },
        });
        const data: any = {
          order: dest_item == null ? 1 : dest_item.order + 1,
          amount: null,
          positionSalaryAmount: null,
          mouthSalaryAmount: null,
          profileId: profile.id,
          posNo: null,
          positionExecutive: null,
          positionType: null,
          positionLevel: null,
          amountSpecial: null,
          orgRoot: null,
          orgChild1: null,
          orgChild2: null,
          orgChild3: null,
          orgChild4: null,
          commandYear: new Date().getFullYear() + 543,
          commandDateAffect: profile.dateLeave,
          commandCode: "16",
          commandName: "พ้นจากราชการ",
          posNoAbb: null,
          isEntry: false,
          positionName: "เกษียณอายุราชการ",
          createdUserId: request.user.sub,
          createdFullName: request.user.name,
          lastUpdateUserId: request.user.sub,
          lastUpdateFullName: request.user.name,
          createdAt: new Date(),
          lastUpdatedAt: new Date(),
          remark: "ประกาศคณะอนุกรรมการสามัญข้าราชการกรุงเทพมหานครสามัญ ลว. 31 มี.ค. 68", // script เกษียณจริง ๆ ให้เอา “วันที่ประกาศเกษียณฉบับแรก” มาลงในเอกสารอ้างอิง
          isGovernment: false,
        };

        const history = new ProfileSalaryHistory();
        Object.assign(history, { ...data, id: undefined });
        data.dateGovernment = profile.dateLeave;
        const savedData = await this.profileSalaryRepository.save(data);

        history.profileSalaryId = savedData.id;
        await this.salaryHistoryRepo.save(history);

        checkOfficer += 1;
      }),
    );

    let checkEmployee: number = 0;
    await Promise.all(
      profileEmployeeLeave.map(async (profile: any) => {
        const dest_item = await this.profileSalaryRepository.findOne({
          where: { profileEmployeeId: profile.id },
          order: { order: "DESC" },
        });
        const data: any = {
          order: dest_item == null ? 1 : dest_item.order + 1,
          amount: null,
          positionSalaryAmount: null,
          mouthSalaryAmount: null,
          profileEmployeeId: profile.id,
          posNo: null,
          positionExecutive: null,
          positionType: null,
          positionLevel: null,
          amountSpecial: null,
          orgRoot: null,
          orgChild1: null,
          orgChild2: null,
          orgChild3: null,
          orgChild4: null,
          commandYear: new Date().getFullYear() + 543,
          commandDateAffect: profile.dateLeave,
          commandCode: "16",
          commandName: "พ้นจากราชการ",
          posNoAbb: null,
          isEntry: false,
          positionName: "เกษียณอายุราชการ",
          createdUserId: request.user.sub,
          createdFullName: request.user.name,
          lastUpdateUserId: request.user.sub,
          lastUpdateFullName: request.user.name,
          createdAt: new Date(),
          lastUpdatedAt: new Date(),
          remark: "ประกาศคณะอนุกรรมการสามัญข้าราชการกรุงเทพมหานครสามัญ ลว. 31 มี.ค. 68", // script เกษียณจริง ๆ ให้เอา “วันที่ประกาศเกษียณฉบับแรก” มาลงในเอกสารอ้างอิง
          isGovernment: false,
        };

        const history = new ProfileSalaryHistory();
        Object.assign(history, { ...data, id: undefined });
        data.dateGovernment = profile.dateLeave;
        const savedData = await this.profileSalaryRepository.save(data);

        history.profileSalaryId = savedData.id;
        await this.salaryHistoryRepo.save(history);

        checkEmployee += 1;
      }),
    );

    // จำนวนคนที่บันทึกลงประวัติตำแหน่ง
    const totalOfficer = profileLeave.length;
    const totalEmployee = profileEmployeeLeave.length;
    // จำนวนคนที่ถูกบันทึกลงประวัติตำแหน่งสำเร็จ
    return new HttpSuccess({
      totalOfficer,
      totalEmployee,
      officerSuccessAmount: checkOfficer,
      successEmployeeAmount: checkEmployee,
    });
  }

  /**
   * API แก้ไขเหตุผลการลาออก และปลดจาก keycloak
   *
   * @summary - แก้ไขเหตุผลการลาออก และปลดจาก keycloak (ADMIN)
   *
   */
  @Get("update/profile/leave-reason")
  async updateRetireReason() {
    const [profileLeave, profileEmployeeLeave, token] = await Promise.all([
      this.profileRepo.find({
        where: {
          isLeave: true,
          isRetirement: true,
          leaveType: IsNull(),
        },
      }),
      this.profileEmployeeRepo.find({
        where: {
          isLeave: true,
          isRetirement: true,
          leaveType: IsNull(),
        },
      }),
      getToken(),
    ]);

    if (!token)
      throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak");

    let checkOfficer: number = 0;
    let notDeleteOfficer: string[] = [];

    await Promise.all(
      profileLeave.map(async (profile) => {
        profile.leaveReason = "เกษียณอายุราชการ";
        profile.leaveType = "RETIRE";
        profile.isActive = false;

        if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) {
          const delUserKeycloak = await deleteUser(profile.keycloak, token);
          if (delUserKeycloak) {
            // profile.keycloak = "";
            profile.isDelete = true;
            profile.roleKeycloaks = [];
            checkOfficer += 1;
          } else {
            // push array not delete
            notDeleteOfficer.push(profile.keycloak);
          }
        }

        await this.profileRepo.save(profile);
      }),
    );

    let checkEmployee: number = 0;
    let notDeleteEmployee: string[] = [];

    await Promise.all(
      profileEmployeeLeave.map(async (profileEmp) => {
        profileEmp.leaveReason = "เกษียณอายุราชการ";
        profileEmp.leaveType = "RETIRE";
        profileEmp.leaveDate = new Date("2025-09-30");
        profileEmp.dateLeave = new Date("2025-09-30");
        profileEmp.lastUpdatedAt = new Date();
        profileEmp.isActive = false;

        if (
          profileEmp.keycloak != null &&
          profileEmp.keycloak != "" &&
          profileEmp.isDelete === false
        ) {
          const delUserKeycloak = await deleteUser(profileEmp.keycloak, token);
          if (delUserKeycloak) {
            // profileEmp.keycloak = "";
            profileEmp.isDelete = true;
            profileEmp.roleKeycloaks = [];
            checkEmployee += 1;
          } else {
            // push array not delete
            notDeleteEmployee.push(profileEmp.keycloak);
          }
        }

        await this.profileEmployeeRepo.save(profileEmp);
      }),
    );

    // loop batch at 50 profiles
    // const chunkSize = 50;
    // for (let i = 0; i < profileLeave.length; i += chunkSize) {
    //   const chunk = profileLeave.slice(i, i + chunkSize);
    //   await Promise.all(
    //     chunk.map(async (profile) => {
    //       profile.leaveReason = "เกษียณอายุราชการ";
    //       profile.isActive = false;

    //       if (profile.keycloak != null && profile.keycloak != "") {
    //         const delUserKeycloak = await deleteUser(profile.keycloak, token);
    //         if (delUserKeycloak) {
    //           profile.keycloak = "";
    //           profile.roleKeycloaks = [];
    //           check += 1;
    //         } else {
    //           // push array not delete
    //           notDelete.push(profile.keycloak);
    //         }
    //       }

    //       await this.profileEmployeeRepo.save(profile);
    //     }),
    //   );
    // }

    // จำนวนคนที่ถูกแก้ไขเหตุผลการลาออก
    const total = profileLeave.length;
    return new HttpSuccess({
      total,
      successOfficerAmount: checkOfficer,
      notDeleteOfficer,
      successEmployeeAmount: checkEmployee,
      notDeleteEmployee,
    });
  }

  /**
   * API สร้าง keycloak ใหม่สำหรับข้าราชการที่รันเกษียณผิด
   *
   * @summary - สร้าง keycloak ใหม่สำหรับข้าราชการที่รันเกษียณผิด (ADMIN)
   *
   */
  @Get("update/org/profile-officer/create-keycloak")
  async createKeycloakForOfficer(@Request() request: RequestWithUser) {
    const [profiles, token] = await Promise.all([
      this.profileRepo.find({
        where: {
          keycloak: IsNull(),
          isActive: true,
        },
        relations: ["roleKeycloaks"],
      }),
      getToken(),
    ]);

    if (!token)
      throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak");

    let check: number = 0;
    for await (const _item of profiles) {
      let password = _item.citizenId;
      if (_item.birthDate != null) {
        const _date = new Date(_item.birthDate.toDateString())
          .getDate()
          .toString()
          .padStart(2, "0");
        const _month = (new Date(_item.birthDate.toDateString()).getMonth() + 1)
          .toString()
          .padStart(2, "0");
        const _year = new Date(_item.birthDate.toDateString()).getFullYear() + 543;
        password = `${_date}${_month}${_year}`;
      }
      const checkUser = await getUserByUsername(_item.citizenId, token);
      let userId: any = "";
      if (checkUser.length == 0) {
        userId = await createUser(
          _item.citizenId,
          password,
          {
            firstName: _item.firstName,
            lastName: _item.lastName,
            // email: _item.email,
          },
          token,
        );
        if (typeof userId !== "string") {
          throw new Error(userId.errorMessage);
        }
      } else {
        userId = checkUser[0].id;
      }

      const list = await getRoles("", token);
      if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server.");
      const result = await addUserRoles(
        userId,
        list.filter((v) => v.id == "8a1a0dc9-304c-4e5b-a90a-65f841048212"),
        token,
      );

      if (!result) {
        throw new Error("Failed. Cannot set user's role.");
      }
      if (typeof userId === "string") {
        _item.keycloak = userId;
      }
      const roleKeycloak = await this.roleKeycloakRepo.find({
        where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" },
      });
      if (_item) {
        _item.roleKeycloaks = Array.from(new Set([..._item.roleKeycloaks, ...roleKeycloak]));
        this.profileRepo.save(_item);
        check += 1;
      }
    }

    const total = profiles.length;

    return new HttpSuccess({ total, successAmount: check });
  }

  /**
   * API สร้าง keycloak ใหม่สำหรับลูกจ้างที่รันเกษียณผิด
   *
   * @summary - สร้าง keycloak ใหม่สำหรับลูกจ้างที่รันเกษียณผิด (ADMIN)
   *
   */
  @Get("update/org/profile-employee/create-keycloak")
  async createKeycloakForEmployee(@Request() request: RequestWithUser) {
    let check: number = 0;
    const profiles = await this.profileEmployeeRepo.find({
      where: {
        keycloak: IsNull(),
        isLeave: false,
        isRetirement: false,
      },
      order: { citizenId: "ASC" },
      relations: ["roleKeycloaks"],
    });

    // ดึงข้อมูลที่ใช้บ่อยก่อน (cache)
    const rolesList = await getRoles();
    if (!Array.isArray(rolesList))
      throw new Error("Failed. Cannot get role(s) data from the server.");

    const roleKeycloak = await this.roleKeycloakRepo.find({
      where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" },
    });

    // Process แบบ batch เพื่อลดการเรียก API ทีละตัว
    const batchSize = 100;
    const batches = [];
    for (let i = 0; i < profiles.length; i += batchSize) {
      batches.push(profiles.slice(i, i + batchSize));
    }
    for (const batch of batches) {
      await Promise.all(
        batch.map(async (_item) => {
          let password = _item.citizenId;
          if (_item.birthDate != null) {
            const _date = new Date(_item.birthDate.toDateString())
              .getDate()
              .toString()
              .padStart(2, "0");
            const _month = (new Date(_item.birthDate.toDateString()).getMonth() + 1)
              .toString()
              .padStart(2, "0");
            const _year = new Date(_item.birthDate.toDateString()).getFullYear() + 543;
            password = `${_date}${_month}${_year}`;
          }

          try {
            const checkUser = await getUserByUsername(_item.citizenId);
            let userId: any = "";
            if (checkUser.length == 0) {
              userId = await createUser(_item.citizenId, password, {
                firstName: _item.firstName,
                lastName: _item.lastName,
              });
              if (typeof userId !== "string") {
                console.error(`Failed to create user for ${_item.citizenId}:`, userId.errorMessage);
                return;
              }
            } else {
              userId = checkUser[0].id;
            }

            const result = await addUserRoles(
              userId,
              rolesList.filter((v) => v.id == "8a1a0dc9-304c-4e5b-a90a-65f841048212"),
            );

            if (!result) {
              console.error(`Failed to set role for user ${_item.citizenId}`);
              return;
            }

            if (typeof userId === "string") {
              _item.keycloak = userId;
            }
            if (_item) {
              _item.roleKeycloaks = Array.from(new Set([..._item.roleKeycloaks, ...roleKeycloak]));
              check += 1;
              _item = await this.profileEmployeeRepo.save(_item);
              // update user attribute in keycloak
              await updateUserAttributes(_item.keycloak, {
                profileId: [_item.id],
                prefix: [_item.prefix || ""],
              });
            }
          } catch (error) {
            console.error(`Error processing ${_item.citizenId}:`, error);
          }
        }),
      );
    }
    const total = profiles.length;
    return new HttpSuccess({ total, successAmount: check });
  }

  /**
   * API ย้ายโครงสร้างแบบร่างไปโครงสร้างปัจจุบัน โดยอ้างอิงตาม rootId
   *
   * @summary - ย้ายโครงสร้างและตำแหน่งจากแบบร่างไปโครงสร้างปัจจุบัน โดยอ้างอิงตาม rootId
   *
   */
  @Post("move-draft-to-current/{rootDnaId}")
  async moveDraftToCurrent(@Path() rootDnaId: string, @Request() request: RequestWithUser) {
    const queryRunner = AppDataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // permission owner only ??
      // this code check...

      // part 1 ข้อมูลโครงสร้าง
      const [drafRevision, currentRevision] = await Promise.all([
        this.orgRevisionRepository.findOne({
          where: {
            orgRevisionIsDraft: true,
            orgRevisionIsCurrent: false,
          },
          select: ["id"],
        }),
        this.orgRevisionRepository.findOne({
          where: {
            orgRevisionIsDraft: false,
            orgRevisionIsCurrent: true,
          },
          select: ["id"],
        }),
      ]);

      if (!drafRevision || !currentRevision)
        return new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");

      const drafRevisionId = drafRevision.id;
      const currentRevisionId = currentRevision.id;

      // ตรวจสอบว่ามี rootDnaId ในโครงสร้างร่าง และในโครงสร้างปัจจุบันหรือไม่
      let [orgRootDraft, orgRootCurrent] = await Promise.all([
        this.orgRootRepository.findOne({
          where: {
            ancestorDNA: rootDnaId,
            orgRevisionId: drafRevisionId,
          },
          // select: ["id"],
        }),
        this.orgRootRepository.findOne({
          where: {
            ancestorDNA: rootDnaId,
            orgRevisionId: currentRevisionId,
          },
          select: ["id"],
        }),
      ]);

      if (!orgRootDraft) return new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้างร่าง");

      let createdCurrentRoot = false;

      // if current record not found, create new one
      if (!orgRootCurrent) {
        // Create new current record using draft's ID
        const newCurrentRoot = queryRunner.manager.create(OrgRoot, {
          ...orgRootDraft,
          id: undefined, // Let database generate new ID
          orgRevisionId: currentRevisionId, // Change to current revision
        });

        const savedRoot = await queryRunner.manager.save(OrgRoot, newCurrentRoot);
        orgRootCurrent = savedRoot; // Use saved record for sync
        createdCurrentRoot = true;
      }

      // Part 1: Differential sync of organization structure (bottom-up)
      // Build mapping incrementally as we process each level

      const allMappings: AllOrgMappings = {
        orgRoot: { byAncestorDNA: new Map(), byDraftId: new Map() },
        orgChild1: { byAncestorDNA: new Map(), byDraftId: new Map() },
        orgChild2: { byAncestorDNA: new Map(), byDraftId: new Map() },
        orgChild3: { byAncestorDNA: new Map(), byDraftId: new Map() },
        orgChild4: { byAncestorDNA: new Map(), byDraftId: new Map() },
      };

      // Track sync statistics for organization nodes
      const orgSyncStats: Record<string, { deleted: number; updated: number; inserted: number }> =
        {};

      // Process from top (Root) to bottom (Child4) to handle foreign key constraints
      // OrgRoot (sync first - no parent dependencies)
      // If we manually created orgRootCurrent, skip syncOrgLevel and set up mapping directly
      // to avoid double insert (syncOrgLevel would try to insert again because IDs don't match)
      let orgRootResult: {
        mapping: OrgIdMapping;
        counts: { deleted: number; updated: number; inserted: number };
      };
      if (createdCurrentRoot && orgRootCurrent && orgRootDraft) {
        // Manually created - set up mapping directly
        const rootMapping: OrgIdMapping = {
          byAncestorDNA: new Map([[orgRootDraft.ancestorDNA, orgRootCurrent.id]]),
          byDraftId: new Map([[orgRootDraft.id, orgRootCurrent.id]]),
        };
        orgRootResult = {
          mapping: rootMapping,
          counts: { deleted: 0, updated: 0, inserted: 1 }, // Count as insert since we created it
        };
      } else {
        // Not manually created - use normal syncOrgLevel flow
        orgRootResult = await this.syncOrgLevel(
          queryRunner,
          OrgRoot,
          this.orgRootRepository,
          drafRevisionId,
          currentRevisionId,
          rootDnaId,
          allMappings,
          orgRootDraft?.id,
          orgRootCurrent?.id,
        );
      }
      allMappings.orgRoot = orgRootResult.mapping;
      orgSyncStats.orgRoot = orgRootResult.counts;

      // Child1 (parent OrgRoot already synced)
      const child1Result = await this.syncOrgLevel(
        queryRunner,
        OrgChild1,
        this.child1Repository,
        drafRevisionId,
        currentRevisionId,
        rootDnaId,
        allMappings,
        orgRootDraft?.id,
        orgRootCurrent?.id,
      );
      allMappings.orgChild1 = child1Result.mapping;
      orgSyncStats.orgChild1 = child1Result.counts;

      // Child2 (parents OrgRoot and Child1 already synced)
      const child2Result = await this.syncOrgLevel(
        queryRunner,
        OrgChild2,
        this.child2Repository,
        drafRevisionId,
        currentRevisionId,
        rootDnaId,
        allMappings,
        orgRootDraft?.id,
        orgRootCurrent?.id,
      );
      allMappings.orgChild2 = child2Result.mapping;
      orgSyncStats.orgChild2 = child2Result.counts;

      // Child3 (parents OrgRoot, Child1, Child2 already synced)
      const child3Result = await this.syncOrgLevel(
        queryRunner,
        OrgChild3,
        this.child3Repository,
        drafRevisionId,
        currentRevisionId,
        rootDnaId,
        allMappings,
        orgRootDraft?.id,
        orgRootCurrent?.id,
      );
      allMappings.orgChild3 = child3Result.mapping;
      orgSyncStats.orgChild3 = child3Result.counts;

      // Child4 (parents OrgRoot, Child1, Child2, Child3 already synced)
      const child4Result = await this.syncOrgLevel(
        queryRunner,
        OrgChild4,
        this.child4Repository,
        drafRevisionId,
        currentRevisionId,
        rootDnaId,
        allMappings,
        orgRootDraft?.id,
        orgRootCurrent?.id,
      );
      allMappings.orgChild4 = child4Result.mapping;
      orgSyncStats.orgChild4 = child4Result.counts;

      // Part 2: Sync position data using new org IDs from Part 1
      // 2.1 Clear current_holderId for affected positions (keep existing logic)
      // Get draft organization IDs under the given rootDnaId to find positions to clear
      const draftOrgIds = {
        orgRoot: [...allMappings.orgRoot.byDraftId.keys()],
        orgChild1: [...allMappings.orgChild1.byDraftId.keys()],
        orgChild2: [...allMappings.orgChild2.byDraftId.keys()],
        orgChild3: [...allMappings.orgChild3.byDraftId.keys()],
        orgChild4: [...allMappings.orgChild4.byDraftId.keys()],
      };

      // Get current organization IDs from the mappings (moved up for reuse)
      const currentOrgIds = {
        orgRoot: [...allMappings.orgRoot.byDraftId.values()],
        orgChild1: [...allMappings.orgChild1.byDraftId.values()],
        orgChild2: [...allMappings.orgChild2.byDraftId.values()],
        orgChild3: [...allMappings.orgChild3.byDraftId.values()],
        orgChild4: [...allMappings.orgChild4.byDraftId.values()],
      };

      // Get draft positions that belong to any org under the rootDnaId
      const posMasterDraft = await this.posMasterRepository.find({
        where: [
          { orgRevisionId: drafRevisionId, orgRootId: In(draftOrgIds.orgRoot) },
          { orgRevisionId: drafRevisionId, orgChild1Id: In(draftOrgIds.orgChild1) },
          { orgRevisionId: drafRevisionId, orgChild2Id: In(draftOrgIds.orgChild2) },
          { orgRevisionId: drafRevisionId, orgChild3Id: In(draftOrgIds.orgChild3) },
          { orgRevisionId: drafRevisionId, orgChild4Id: In(draftOrgIds.orgChild4) },
        ],
      });

      // Special case: If draft has no positions, delete all current positions
      if (posMasterDraft.length <= 0) {
        // Fetch current positions
        const posMasterCurrent = await this.posMasterRepository.find({
          relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"],
          where: [
            { orgRevisionId: currentRevisionId, orgRootId: In(currentOrgIds.orgRoot) },
            { orgRevisionId: currentRevisionId, orgChild1Id: In(currentOrgIds.orgChild1) },
            { orgRevisionId: currentRevisionId, orgChild2Id: In(currentOrgIds.orgChild2) },
            { orgRevisionId: currentRevisionId, orgChild3Id: In(currentOrgIds.orgChild3) },
            { orgRevisionId: currentRevisionId, orgChild4Id: In(currentOrgIds.orgChild4) },
          ],
        });

        if (posMasterCurrent.length > 0) {
          const toDeleteIds = posMasterCurrent.map((p) => p.id);

          // Cascade delete positions first
          await queryRunner.manager.delete(Position, { posMasterId: In(toDeleteIds) });

          // Then delete posMaster records
          await queryRunner.manager.delete(PosMaster, toDeleteIds);

          // Save history
          const deleteHistoryOps = posMasterCurrent.map((pos) => ({
            posMasterDnaId: pos.ancestorDNA,
            profileId: null,
            pm: {
              prefix: null,
              firstName: null,
              lastName: null,
              position: null,
              posType: null,
              posLevel: null,
              posExecutive: null,
              profileId: null,
              shortName: pos
                ? [
                    pos.orgChild4?.orgChild4ShortName,
                    pos.orgChild3?.orgChild3ShortName,
                    pos.orgChild2?.orgChild2ShortName,
                    pos.orgChild1?.orgChild1ShortName,
                    pos.orgRoot?.orgRootShortName,
                  ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ??
                  null
                : null,
              posMasterNoPrefix: pos.posMasterNoPrefix ?? null,
              posMasterNo: pos.posMasterNo != null ? String(pos.posMasterNo) : null,
              posMasterNoSuffix: pos.posMasterNoSuffix ?? null,
              rootDnaId: pos?.orgRoot?.ancestorDNA ?? null,
              child1DnaId: pos?.orgChild1?.ancestorDNA ?? null,
              child2DnaId: pos?.orgChild2?.ancestorDNA ?? null,
              child3DnaId: pos?.orgChild3?.ancestorDNA ?? null,
              child4DnaId: pos?.orgChild4?.ancestorDNA ?? null,
            } as SavePosMasterHistory,
          }));
          await BatchSavePosMasterHistoryOfficer(queryRunner, deleteHistoryOps);
        }

        // Build summary for this special case
        const summary = {
          message: "ย้ายโครงสร้างสำเร็จ",
          organization: {
            orgRoot: orgSyncStats.orgRoot,
            orgChild1: orgSyncStats.orgChild1,
            orgChild2: orgSyncStats.orgChild2,
            orgChild3: orgSyncStats.orgChild3,
            orgChild4: orgSyncStats.orgChild4,
          },
          positionMaster: {
            deleted: posMasterCurrent.length,
            updated: 0,
            inserted: 0,
          },
          position: { deleted: 0, updated: 0, inserted: 0 },
        };

        await queryRunner.commitTransaction();
        return new HttpSuccess(summary);
      }

      // Clear current_holderId for positions that will have new holders
      const nextHolderIds = posMasterDraft
        .filter((x) => x.next_holderId != null)
        .map((x) => x.next_holderId) as string[];

      if (nextHolderIds.length > 0) {
        // FIX: Fetch positions first before updating (to avoid race condition)
        const posMastersToUpdate = await queryRunner.manager.find(PosMaster, {
          relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"],
          where: {
            orgRevisionId: currentRevisionId,
            current_holderId: In(nextHolderIds),
          },
        });

        // Save history BEFORE clearing current_holderId
        const historyOps = posMastersToUpdate
          .filter((x) => x.orgRootId != orgRootCurrent?.id)
          .map((pos) => ({
            posMasterDnaId: pos.ancestorDNA,
            profileId: null,
            pm: {
              prefix: null,
              firstName: null,
              lastName: null,
              position: null,
              posType: null,
              posLevel: null,
              posExecutive: null,
              profileId: null,
              shortName: pos
                ? [
                    pos.orgChild4?.orgChild4ShortName,
                    pos.orgChild3?.orgChild3ShortName,
                    pos.orgChild2?.orgChild2ShortName,
                    pos.orgChild1?.orgChild1ShortName,
                    pos.orgRoot?.orgRootShortName,
                  ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ??
                  null
                : null,
              posMasterNoPrefix: pos.posMasterNoPrefix ?? null,
              posMasterNo: pos.posMasterNo != null ? String(pos.posMasterNo) : null,
              posMasterNoSuffix: pos.posMasterNoSuffix ?? null,
              rootDnaId: pos?.orgRoot?.ancestorDNA ?? null,
              child1DnaId: pos?.orgChild1?.ancestorDNA ?? null,
              child2DnaId: pos?.orgChild2?.ancestorDNA ?? null,
              child3DnaId: pos?.orgChild3?.ancestorDNA ?? null,
              child4DnaId: pos?.orgChild4?.ancestorDNA ?? null,
            } as SavePosMasterHistory,
          }));
        await BatchSavePosMasterHistoryOfficer(queryRunner, historyOps);

        // Now clear current_holderId
        await queryRunner.manager.update(
          PosMaster,
          {
            orgRevisionId: currentRevisionId,
            current_holderId: In(nextHolderIds),
          },
          { current_holderId: null, isSit: false },
        );
      }

      // 2.2 Fetch current positions for comparison
      const posMasterCurrent = await this.posMasterRepository.find({
        relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"],
        where: [
          { orgRevisionId: currentRevisionId, orgRootId: In(currentOrgIds.orgRoot) },
          { orgRevisionId: currentRevisionId, orgChild1Id: In(currentOrgIds.orgChild1) },
          { orgRevisionId: currentRevisionId, orgChild2Id: In(currentOrgIds.orgChild2) },
          { orgRevisionId: currentRevisionId, orgChild3Id: In(currentOrgIds.orgChild3) },
          { orgRevisionId: currentRevisionId, orgChild4Id: In(currentOrgIds.orgChild4) },
        ],
      });

      // Build lookup map
      const currentByDNA = new Map(posMasterCurrent.map((p) => [p.ancestorDNA, p]));

      // 2.3 Batch DELETE: positions in current but not in draft
      const toDelete = posMasterCurrent.filter(
        (curr) => !posMasterDraft.some((d) => d.ancestorDNA === curr.ancestorDNA),
      );

      if (toDelete.length > 0) {
        const toDeleteIds = toDelete.map((p) => p.id);

        // Cascade delete positions first
        await queryRunner.manager.delete(Position, { posMasterId: In(toDeleteIds) });

        // Then delete posMaster records
        await queryRunner.manager.delete(PosMaster, toDeleteIds);

        const deleteHistoryOps = toDelete.map((pos) => ({
          posMasterDnaId: pos.ancestorDNA,
          profileId: null,
          pm: {
            prefix: null,
            firstName: null,
            lastName: null,
            position: null,
            posType: null,
            posLevel: null,
            posExecutive: null,
            profileId: null,
            shortName: pos
              ? [
                  pos.orgChild4?.orgChild4ShortName,
                  pos.orgChild3?.orgChild3ShortName,
                  pos.orgChild2?.orgChild2ShortName,
                  pos.orgChild1?.orgChild1ShortName,
                  pos.orgRoot?.orgRootShortName,
                ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ??
                null
              : null,
            posMasterNoPrefix: pos.posMasterNoPrefix ?? null,
            posMasterNo: pos.posMasterNo != null ? String(pos.posMasterNo) : null,
            posMasterNoSuffix: pos.posMasterNoSuffix ?? null,
            rootDnaId: pos?.orgRoot?.ancestorDNA ?? null,
            child1DnaId: pos?.orgChild1?.ancestorDNA ?? null,
            child2DnaId: pos?.orgChild2?.ancestorDNA ?? null,
            child3DnaId: pos?.orgChild3?.ancestorDNA ?? null,
            child4DnaId: pos?.orgChild4?.ancestorDNA ?? null,
          } as SavePosMasterHistory,
        }));
        await BatchSavePosMasterHistoryOfficer(queryRunner, deleteHistoryOps);
      }

      // 2.4 Process draft positions (UPDATE or INSERT)
      const toUpdate: Partial<PosMaster>[] = [];
      const toInsert: any[] = [];

      // Track draft PosMaster ID to current PosMaster ID mapping for position sync
      // Type: Map<draftPosMasterId, [currentPosMasterId, nextHolderId]>
      const posMasterMapping: Map<string, [string, string | null | undefined]> = new Map();

      // Collect positions where next_holderId is null for batch history saving
      const nullHolderDraftPosIds: string[] = [];

      for (const draftPos of posMasterDraft) {
        const current = currentByDNA.get(draftPos.ancestorDNA);

        // Map organization IDs using new IDs from Part 1
        const orgRootId = this.resolveOrgId(draftPos.orgRootId ?? null, allMappings.orgRoot);
        const orgChild1Id = this.resolveOrgId(draftPos.orgChild1Id ?? null, allMappings.orgChild1);
        const orgChild2Id = this.resolveOrgId(draftPos.orgChild2Id ?? null, allMappings.orgChild2);
        const orgChild3Id = this.resolveOrgId(draftPos.orgChild3Id ?? null, allMappings.orgChild3);
        const orgChild4Id = this.resolveOrgId(draftPos.orgChild4Id ?? null, allMappings.orgChild4);

        if (current) {
          // UPDATE existing position
          toUpdate.push({
            id: current.id,
            ancestorDNA: current.ancestorDNA,
            createdAt: draftPos.createdAt,
            createdUserId: draftPos.createdUserId,
            createdFullName: draftPos.createdFullName,
            lastUpdatedAt: new Date(),
            lastUpdateUserId: request.user.sub,
            lastUpdateFullName: request.user.name,
            posMasterNoPrefix: draftPos.posMasterNoPrefix,
            posMasterNoSuffix: draftPos.posMasterNoSuffix,
            posMasterNo: draftPos.posMasterNo,
            posMasterOrder: draftPos.posMasterOrder,
            orgRevisionId: currentRevisionId,
            orgRootId,
            orgChild1Id,
            orgChild2Id,
            orgChild3Id,
            orgChild4Id,
            current_holderId: draftPos.next_holderId,
            next_holderId: null,
            isSit: draftPos.isSit,
            reason: draftPos.reason,
            isDirector: draftPos.isDirector,
            isStaff: draftPos.isStaff,
            positionSign: draftPos.positionSign,
            statusReport: "DONE",
            isCondition: draftPos.isCondition,
            conditionReason: draftPos.conditionReason,
          });

          if (draftPos.next_holderId === null) {
            nullHolderDraftPosIds.push(draftPos.id);
          }

          // Track mapping for position sync
          posMasterMapping.set(draftPos.id, [current.id, draftPos.next_holderId]);
        } else {
          // INSERT new position
          const newPosMaster = queryRunner.manager.create(PosMaster, {
            ...draftPos,
            id: undefined,
            orgRevisionId: currentRevisionId,
            orgRootId,
            orgChild1Id,
            orgChild2Id,
            orgChild3Id,
            orgChild4Id,
            current_holderId: draftPos.next_holderId,
            next_holderId: null,
            statusReport: "DONE",
          });

          toInsert.push(newPosMaster);
        }
      }

      // Batch save updates and inserts
      if (toUpdate.length > 0) {
        await queryRunner.manager.save(PosMaster, toUpdate);
      }
      if (toInsert.length > 0) {
        const saved = await queryRunner.manager.save(toInsert);

        // Track mapping for newly inserted posMasters
        // saved is an array, map each to its draft ID
        if (Array.isArray(saved)) {
          for (let i = 0; i < saved.length; i++) {
            const draftPos = posMasterDraft.filter((d) => !currentByDNA.has(d.ancestorDNA))[i];
            if (draftPos && saved[i]) {
              posMasterMapping.set(draftPos.id, [saved[i].id, draftPos.next_holderId]);
            }
          }
        }
      }

      // 2.4.1 Save PosMasterHistory for positions where next_holderId was cleared (null)
      // These need org relations to populate shortName, rootDnaId, child*DnaId fields
      if (nullHolderDraftPosIds.length > 0) {
        const nullHolderCurrentPosIds = nullHolderDraftPosIds
          .map((draftPosId) => posMasterMapping.get(draftPosId)?.[0] ?? null)
          .filter((currentPosId): currentPosId is string => currentPosId !== null);

        const nullHolderPosMasters = await queryRunner.manager.find(PosMaster, {
          where: { id: In(nullHolderCurrentPosIds) },
          relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"],
        });
        const nullHolderMap = new Map(nullHolderPosMasters.map((pm) => [pm.id, pm as any]));

        const nullHolderHistoryOps = posMasterDraft
          .filter((d) => nullHolderDraftPosIds.includes(d.id))
          .map((draftPos) => {
            const currentPosId = posMasterMapping.get(draftPos.id)?.[0] ?? null;
            const pmWithRelations = currentPosId ? nullHolderMap.get(currentPosId) : null;
            return {
              posMasterDnaId: draftPos.ancestorDNA,
              profileId: null as string | null,
              pm: {
                prefix: null,
                firstName: null,
                lastName: null,
                position: null,
                posType: null,
                posLevel: null,
                posExecutive: null,
                profileId: null,
                shortName: pmWithRelations
                  ? [
                      pmWithRelations.orgChild4?.orgChild4ShortName,
                      pmWithRelations.orgChild3?.orgChild3ShortName,
                      pmWithRelations.orgChild2?.orgChild2ShortName,
                      pmWithRelations.orgChild1?.orgChild1ShortName,
                      pmWithRelations.orgRoot?.orgRootShortName,
                    ].find(
                      (s: string | undefined) => typeof s === "string" && s.trim().length > 0,
                    ) ?? null
                  : null,
                posMasterNoPrefix: draftPos.posMasterNoPrefix ?? null,
                posMasterNo: draftPos.posMasterNo != null ? String(draftPos.posMasterNo) : null,
                posMasterNoSuffix: draftPos.posMasterNoSuffix ?? null,
                rootDnaId: pmWithRelations?.orgRoot?.ancestorDNA ?? null,
                child1DnaId: pmWithRelations?.orgChild1?.ancestorDNA ?? null,
                child2DnaId: pmWithRelations?.orgChild2?.ancestorDNA ?? null,
                child3DnaId: pmWithRelations?.orgChild3?.ancestorDNA ?? null,
                child4DnaId: pmWithRelations?.orgChild4?.ancestorDNA ?? null,
              } as SavePosMasterHistory,
            };
          });

        await BatchSavePosMasterHistoryOfficer(queryRunner, nullHolderHistoryOps);
      }

      // 2.5 Sync positions table for all affected posMasters (BATCH operation for performance)
      const positionSyncStats = await this.syncAllPositionsBatch(
        queryRunner,
        posMasterMapping,
        drafRevisionId,
        currentRevisionId,
      );

      // Build comprehensive summary
      const summary = {
        message: "ย้ายโครงสร้างสำเร็จ",
        organization: {
          orgRoot: orgSyncStats.orgRoot,
          orgChild1: orgSyncStats.orgChild1,
          orgChild2: orgSyncStats.orgChild2,
          orgChild3: orgSyncStats.orgChild3,
          orgChild4: orgSyncStats.orgChild4,
        },
        positionMaster: {
          deleted: toDelete.length,
          updated: toUpdate.length,
          inserted: toInsert.length,
        },
        position: positionSyncStats,
      };

      await queryRunner.commitTransaction();

      // Clear Redis cache after successful publish
      const redis = require("redis");
      const { promisify } = require("util");
      const redisClient = redis.createClient({
        host: process.env.REDIS_HOST || "localhost",
        port: parseInt(process.env.REDIS_PORT || "6379"),
      });
      const keysAsync = promisify(redisClient.keys).bind(redisClient);
      const delAsync = promisify(redisClient.del).bind(redisClient);

      try {
        const [posMasterKeys, userKeys, orgKeys, menuKeys, roleKeys] = await Promise.all([
          keysAsync("posMaster_*"),
          keysAsync("user_*"),
          keysAsync("org_*"),
          keysAsync("menu_*"),
          keysAsync("role_*"),
        ]);

        if (posMasterKeys.length > 0) await delAsync(...posMasterKeys);
        if (userKeys.length > 0) await delAsync(...userKeys);
        if (orgKeys.length > 0) await delAsync(...orgKeys);
        if (menuKeys.length > 0) await delAsync(...menuKeys);
        if (roleKeys.length > 0) await delAsync(...roleKeys);

        console.log(`[moveDraftToCurrent] Cleared cache: posMaster=${posMasterKeys.length}, user=${userKeys.length}, org=${orgKeys.length}, menu=${menuKeys.length}, role=${roleKeys.length}`);
      } catch (err) {
        console.error("[moveDraftToCurrent] Error clearing cache:", err);
      } finally {
        redisClient.quit();
      }

      // Invalidate memory cache
      orgStructureCache.invalidate(currentRevisionId);

      return new HttpSuccess(summary);
    } catch (error) {
      console.error("Error moving draft to current:", error);
      await queryRunner.rollbackTransaction();
      throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดในการย้ายโครงสร้าง");
    } finally {
      if (queryRunner.isTransactionActive) {
        await queryRunner.rollbackTransaction();
      }
      await queryRunner.release();
    }
  }

  /**
   * Helper function: Map draft ID to current ID using the mapping
   */
  private resolveOrgId(draftId: string | null, mapping: OrgIdMapping): string | null {
    if (!draftId) return null;
    return mapping.byDraftId.get(draftId) ?? null;
  }

  private resolveRequiredOrgId(
    draftId: string | null | undefined,
    mapping: OrgIdMapping,
    fieldName: string,
    entityName: string,
    entityDna: string,
    rootDnaId: string,
  ): string | null {
    if (!draftId) return null;

    const mappedId = mapping.byDraftId.get(draftId) ?? null;
    if (mappedId) return mappedId;

    throw new HttpError(
      HttpStatusCode.INTERNAL_SERVER_ERROR,
      `ไม่สามารถ map ${fieldName} ของ ${entityName} (${entityDna}) ใน rootDnaId ${rootDnaId} ได้`,
    );
  }

  private getMappedParentIds(
    entityClass: any,
    draftNode: any,
    parentMappings: AllOrgMappings | undefined,
    rootDnaId: string,
  ): Partial<{
    orgRootId: string | null;
    orgChild1Id: string | null;
    orgChild2Id: string | null;
    orgChild3Id: string | null;
  }> {
    if (entityClass === OrgRoot) {
      return {};
    }

    if (!parentMappings) {
      throw new HttpError(
        HttpStatusCode.INTERNAL_SERVER_ERROR,
        `ไม่พบข้อมูล mapping ของโครงสร้างสำหรับ rootDnaId ${rootDnaId}`,
      );
    }

    const mappedParentIds: Partial<{
      orgRootId: string | null;
      orgChild1Id: string | null;
      orgChild2Id: string | null;
      orgChild3Id: string | null;
    }> = {};

    mappedParentIds.orgRootId = this.resolveRequiredOrgId(
      draftNode.orgRootId,
      parentMappings.orgRoot,
      "orgRootId",
      entityClass.name,
      draftNode.ancestorDNA,
      rootDnaId,
    );

    if (entityClass === OrgChild2 || entityClass === OrgChild3 || entityClass === OrgChild4) {
      mappedParentIds.orgChild1Id = this.resolveRequiredOrgId(
        draftNode.orgChild1Id,
        parentMappings.orgChild1,
        "orgChild1Id",
        entityClass.name,
        draftNode.ancestorDNA,
        rootDnaId,
      );
    }

    if (entityClass === OrgChild3 || entityClass === OrgChild4) {
      mappedParentIds.orgChild2Id = this.resolveRequiredOrgId(
        draftNode.orgChild2Id,
        parentMappings.orgChild2,
        "orgChild2Id",
        entityClass.name,
        draftNode.ancestorDNA,
        rootDnaId,
      );
    }

    if (entityClass === OrgChild4) {
      mappedParentIds.orgChild3Id = this.resolveRequiredOrgId(
        draftNode.orgChild3Id,
        parentMappings.orgChild3,
        "orgChild3Id",
        entityClass.name,
        draftNode.ancestorDNA,
        rootDnaId,
      );
    }

    return mappedParentIds;
  }

  /**
   * Helper function: Cascade delete positions before deleting org node
   */
  private async cascadeDeletePositions(
    queryRunner: any,
    node: any,
    entityClass: any,
  ): Promise<void> {
    const whereClause: any = {
      orgRevisionId: node.orgRevisionId,
    };

    // Determine which FK field to use based on entity type
    if (entityClass === OrgRoot) {
      whereClause.orgRootId = node.id;
    } else if (entityClass === OrgChild1) {
      whereClause.orgChild1Id = node.id;
    } else if (entityClass === OrgChild2) {
      whereClause.orgChild2Id = node.id;
    } else if (entityClass === OrgChild3) {
      whereClause.orgChild3Id = node.id;
    } else if (entityClass === OrgChild4) {
      whereClause.orgChild4Id = node.id;
    }

    await queryRunner.manager.delete(PosMaster, whereClause);
  }

  /**
   * Helper function: Generic differential sync for each org level
   * Performs DELETE (nodes not in draft), UPDATE (existing nodes), INSERT (new nodes)
   */
  private async syncOrgLevel<T extends OrgRoot | OrgChild1 | OrgChild2 | OrgChild3 | OrgChild4>(
    queryRunner: any,
    entityClass: any,
    repository: any,
    draftRevisionId: string,
    currentRevisionId: string,
    rootDnaId: string,
    parentMappings?: AllOrgMappings,
    draftOrgRootId?: string,
    currentOrgRootId?: string,
  ): Promise<{
    mapping: OrgIdMapping;
    counts: { deleted: number; updated: number; inserted: number };
  }> {
    // 1. Fetch draft and current nodes under the given rootDnaId
    // Build WHERE condition for draft nodes
    const draftWhere: any = {
      orgRevisionId: draftRevisionId,
    };
    if (
      draftOrgRootId &&
      (entityClass === OrgChild1 ||
        entityClass === OrgChild2 ||
        entityClass === OrgChild3 ||
        entityClass === OrgChild4)
    ) {
      draftWhere.orgRootId = draftOrgRootId;
    } else if (entityClass === OrgRoot) {
      draftWhere.id = draftOrgRootId;
    }

    // Build WHERE condition for current nodes
    const currentWhere: any = {
      orgRevisionId: currentRevisionId,
    };
    if (
      currentOrgRootId &&
      (entityClass === OrgChild1 ||
        entityClass === OrgChild2 ||
        entityClass === OrgChild3 ||
        entityClass === OrgChild4)
    ) {
      currentWhere.orgRootId = currentOrgRootId;
    } else if (entityClass === OrgRoot) {
      currentWhere.id = currentOrgRootId;
    }

    const [draftNodes, currentNodes] = await Promise.all([
      repository.find({ where: draftWhere }),
      repository.find({ where: currentWhere }),
    ]);

    // 2. Build lookup maps for efficient matching by ancestorDNA
    const draftByDNA = new Map(draftNodes.map((n: any) => [n.ancestorDNA, n]));
    const currentByDNA = new Map(currentNodes.map((n: any) => [n.ancestorDNA, n]));

    const mapping: OrgIdMapping = {
      byAncestorDNA: new Map(),
      byDraftId: new Map(),
    };

    // 3. DELETE: Current nodes not in draft (cascade delete positions first)
    const toDelete = currentNodes.filter((curr: any) => !draftByDNA.has(curr.ancestorDNA));
    for (const node of toDelete) {
      // Cascade delete positions first
      await this.cascadeDeletePositions(queryRunner, node, entityClass);
      await queryRunner.manager.delete(entityClass, node.id);
    }

    // 4. UPDATE: Nodes that exist in both draft and current (matched by ancestorDNA)
    const toUpdate = draftNodes.filter((draft: any) => currentByDNA.has(draft.ancestorDNA));
    for (const draft of toUpdate) {
      const current: any = currentByDNA.get(draft.ancestorDNA)!;

      // Build update data with mapped parent IDs
      const updateData: any = {
        ...draft,
        id: current.id,
        orgRevisionId: currentRevisionId,
        ...this.getMappedParentIds(entityClass, draft, parentMappings, rootDnaId),
      };

      await queryRunner.manager.update(entityClass, current.id, updateData);

      mapping.byAncestorDNA.set(draft.ancestorDNA, current.id);
      mapping.byDraftId.set(draft.id, current.id);
    }

    // 5. INSERT: Draft nodes not in current
    const toInsert = draftNodes.filter((draft: any) => !currentByDNA.has(draft.ancestorDNA));
    for (const draft of toInsert) {
      const newNode: any = queryRunner.manager.create(entityClass, {
        ...draft,
        id: undefined,
        orgRevisionId: currentRevisionId,
        ...this.getMappedParentIds(entityClass, draft, parentMappings, rootDnaId),
      });

      const saved = await queryRunner.manager.save(newNode);

      mapping.byAncestorDNA.set(draft.ancestorDNA, saved.id);
      mapping.byDraftId.set(draft.id, saved.id);
    }

    return {
      mapping,
      counts: {
        deleted: toDelete.length,
        updated: toUpdate.length,
        inserted: toInsert.length,
      },
    };
  }

  /**
   * Batch version: Sync positions for ALL posMasters in a single operation
   * This significantly reduces database round trips for large organizations
   */
  private async syncAllPositionsBatch(
    queryRunner: any,
    posMasterMapping: Map<string, [string, string | null | undefined]>,
    _draftRevisionId: string,
    _currentRevisionId: string,
  ): Promise<{ deleted: number; updated: number; inserted: number }> {
    // Extract draft and current posMaster IDs
    const draftPosMasterIds = Array.from(posMasterMapping.keys());
    const currentPosMasterIds = Array.from(posMasterMapping.values()).map(
      ([currentId]) => currentId,
    );

    if (draftPosMasterIds.length === 0) {
      return { deleted: 0, updated: 0, inserted: 0 };
    }

    // Fetch draft PosMasters with relations for history tracking
    const draftPosMasters = await queryRunner.manager.find(PosMaster, {
      where: { id: In(draftPosMasterIds) },
      relations: [
        "orgRoot",
        "orgChild1",
        "orgChild2",
        "orgChild3",
        "orgChild4",
        "next_holder",
        "next_holder.posType",
        "next_holder.posLevel",
      ],
    });

    // Fetch ALL positions for ALL posMasters in just 2 queries
    const [allDraftPositions, allCurrentPositions] = await Promise.all([
      queryRunner.manager.find(Position, {
        where: {
          posMasterId: In(draftPosMasterIds),
        },
        relations: ["posType", "posLevel", "posExecutive"],
        order: { orderNo: "ASC" },
      }),
      queryRunner.manager.find(Position, {
        where: {
          posMasterId: In(currentPosMasterIds),
        },
        relations: ["posType", "posLevel", "posExecutive"],
      }),
    ]);

    // Group positions by posMasterId for processing
    const draftPositionsByMaster = new Map<string, any[]>();
    for (const pos of allDraftPositions) {
      if (!draftPositionsByMaster.has(pos.posMasterId)) {
        draftPositionsByMaster.set(pos.posMasterId, []);
      }
      draftPositionsByMaster.get(pos.posMasterId)!.push(pos);
    }

    const currentPositionsByMaster = new Map<string, any[]>();
    for (const pos of allCurrentPositions) {
      if (!currentPositionsByMaster.has(pos.posMasterId)) {
        currentPositionsByMaster.set(pos.posMasterId, []);
      }
      currentPositionsByMaster.get(pos.posMasterId)!.push(pos);
    }

    // Collect all operations
    const allToDelete: string[] = [];
    const allToDeleteHistory: string[] = [];
    const allToUpdate: Array<{ id: string; data: any }> = [];
    const allToInsert: Array<any> = [];
    const profileUpdates: Map<string, any> = new Map();

    // Collect position and posMaster data for delete history tracking
    const deleteHistoryData: Array<{
      position: any;
      posMaster: PosMaster;
    }> = [];

    // Create a map for quick lookup of draft PosMasters with relations
    const draftPosMasterMap = new Map(draftPosMasters.map((pm: PosMaster) => [pm.id, pm]));

    // Collect PosMasterHistory calls for selected positions
    const historyCalls: Array<{
      ancestorDNA: string;
      profileId: string | null;
      historyData: SavePosMasterHistory;
    }> = [];

    // Process each posMaster mapping
    for (const [draftPosMasterId, [currentPosMasterId, nextHolderId]] of posMasterMapping) {
      const draftPositions = draftPositionsByMaster.get(draftPosMasterId) || [];
      const currentPositions = currentPositionsByMaster.get(currentPosMasterId) || [];

      // If no draft positions, mark all current positions for deletion
      if (draftPositions.length === 0) {
        allToDelete.push(...currentPositions.map((p: any) => p.id));
        allToDeleteHistory.push(...currentPositions.map((p: any) => p.ancestorDNA));
        // Collect data for history tracking
        const pm = draftPosMasterMap.get(draftPosMasterId) as PosMaster;
        for (const pos of currentPositions) {
          deleteHistoryData.push({ position: pos, posMaster: pm });
        }
        continue;
      }

      // Build map for tracking
      const currentByOrderNo = new Map(currentPositions.map((p: any) => [p.orderNo, p]));
      const draftOrderNos = new Set(draftPositions.map((p: any) => p.orderNo));

      // Mark for deletion: current positions not in draft (by orderNo)
      const pm = draftPosMasterMap.get(draftPosMasterId) as PosMaster;
      for (const currentPos of currentPositions) {
        if (!draftOrderNos.has(currentPos.orderNo)) {
          allToDelete.push(currentPos.id);
          allToDeleteHistory.push(currentPos.ancestorDNA);
          // Collect data for history tracking
          deleteHistoryData.push({ position: currentPos, posMaster: pm });
        }
      }

      // Process UPDATE and INSERT
      for (const draftPos of draftPositions) {
        const current = currentByOrderNo.get(draftPos.orderNo);

        if (current) {
          // UPDATE existing position - collect for batch update
          allToUpdate.push({
            id: current.id,
            data: {
              positionName: draftPos.positionName,
              positionField: draftPos.positionField,
              posTypeId: draftPos.posTypeId,
              posLevelId: draftPos.posLevelId,
              posExecutiveId: draftPos.posExecutiveId,
              positionExecutiveField: draftPos.positionExecutiveField,
              positionArea: draftPos.positionArea,
              isSpecial: draftPos.isSpecial,
              orderNo: draftPos.orderNo,
              positionIsSelected: draftPos.positionIsSelected,
              lastUpdateFullName: draftPos.lastUpdateFullName,
              lastUpdatedAt: new Date(),
            },
          });
        } else {
          // INSERT new position - collect for batch insert
          allToInsert.push({
            ...draftPos,
            id: undefined,
            posMasterId: currentPosMasterId,
          });
        }

        // Collect history data for the selected position
        const draftPosMaster = draftPosMasterMap.get(draftPosMasterId) as any;

        // Collect profile update for the selected position
        // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit
        if (nextHolderId != null && draftPos.positionIsSelected) {
          const _null: any = null;
          profileUpdates.set(nextHolderId, {
            posMasterNo: draftPosMaster
              ? getPosMasterNo(draftPosMaster as PosMaster) ?? _null
              : _null,
            org: draftPosMaster ? getOrgFullName(draftPosMaster as PosMaster) ?? _null : _null,
          });
        }
        
        if (nextHolderId != null && draftPos.positionIsSelected) {
          // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ
          if (!draftPosMaster?.isSit) {
            const existing = profileUpdates.get(nextHolderId) || {};
            existing.position = draftPos.positionName;
            existing.posTypeId = draftPos.posTypeId;
            existing.posLevelId = draftPos.posLevelId;
            existing.positionField = draftPos.positionField ?? null;
            existing.posExecutive = (draftPos as any).posExecutive?.posExecutiveName ?? null;
            existing.positionArea = draftPos.positionArea ?? null;
            existing.positionExecutiveField = draftPos.positionExecutiveField ?? null;
            profileUpdates.set(nextHolderId, existing);
          }

          // ยังบันทึกประวัติคนครอง
          if (draftPosMaster && draftPosMaster.ancestorDNA) {
            // Find the selected position from draft positions
            const selectedPos =
              draftPositions.find((p) => p.positionIsSelected === true) || draftPos;
            historyCalls.push({
              ancestorDNA: draftPosMaster.ancestorDNA,
              profileId: nextHolderId,
              historyData: {
                prefix: draftPosMaster.next_holder?.prefix ?? null,
                firstName: draftPosMaster.next_holder?.firstName ?? null,
                lastName: draftPosMaster.next_holder?.lastName ?? null,
                // isSit = true ดึงค่าจาก profile
                position: draftPosMaster?.isSit ? draftPosMaster.next_holder?.position ?? null : selectedPos.positionName ?? null,
                posType: draftPosMaster?.isSit ? draftPosMaster.next_holder?.posType?.posTypeName ?? null : (selectedPos as any).posType?.posTypeName ?? null,
                posLevel: draftPosMaster?.isSit ? draftPosMaster.next_holder?.posLevel?.posLevelName ?? null : (selectedPos as any).posLevel?.posLevelName ?? null,
                posExecutive: draftPosMaster?.isSit ? draftPosMaster.next_holder?.posExecutive ?? null : (selectedPos as any).posExecutive?.posExecutiveName ?? null,
                profileId: nextHolderId,
                rootDnaId: draftPosMaster.orgRoot?.ancestorDNA ?? null,
                child1DnaId: draftPosMaster.orgChild1?.ancestorDNA ?? null,
                child2DnaId: draftPosMaster.orgChild2?.ancestorDNA ?? null,
                child3DnaId: draftPosMaster.orgChild3?.ancestorDNA ?? null,
                child4DnaId: draftPosMaster.orgChild4?.ancestorDNA ?? null,
                shortName:
                  [
                    draftPosMaster.orgChild4?.orgChild4ShortName,
                    draftPosMaster.orgChild3?.orgChild3ShortName,
                    draftPosMaster.orgChild2?.orgChild2ShortName,
                    draftPosMaster.orgChild1?.orgChild1ShortName,
                    draftPosMaster.orgRoot?.orgRootShortName,
                  ].find((s) => typeof s === "string" && s.trim().length > 0) ?? null,
                posMasterNoPrefix: draftPosMaster.posMasterNoPrefix ?? null,
                posMasterNo: draftPosMaster.posMasterNo ?? null,
                posMasterNoSuffix: draftPosMaster.posMasterNoSuffix ?? null,
              },
            });
          }
        }
      }
    }

    // Execute bulk operations
    let deletedCount = 0;
    let updatedCount = 0;
    let insertedCount = 0;

    // Bulk DELETE
    if (allToDelete.length > 0) {
      await queryRunner.manager.delete(Position, allToDelete);
      const deleteOps = deleteHistoryData.map(({ position, posMaster }) => ({
        posMasterDnaId: position.ancestorDNA,
        profileId: null,
        pm: {
          prefix: null,
          firstName: null,
          lastName: null,
          position: null,
          posType: null,
          posLevel: null,
          posExecutive: null,
          profileId: null,
          rootDnaId: posMaster?.orgRoot?.ancestorDNA ?? null,
          child1DnaId: posMaster?.orgChild1?.ancestorDNA ?? null,
          child2DnaId: posMaster?.orgChild2?.ancestorDNA ?? null,
          child3DnaId: posMaster?.orgChild3?.ancestorDNA ?? null,
          child4DnaId: posMaster?.orgChild4?.ancestorDNA ?? null,
          shortName: posMaster
            ? [
                posMaster.orgChild4?.orgChild4ShortName,
                posMaster.orgChild3?.orgChild3ShortName,
                posMaster.orgChild2?.orgChild2ShortName,
                posMaster.orgChild1?.orgChild1ShortName,
                posMaster.orgRoot?.orgRootShortName,
              ].find((s) => typeof s === "string" && s.trim().length > 0) ?? null
            : null,
          posMasterNoPrefix: posMaster?.posMasterNoPrefix ?? null,
          posMasterNo: posMaster?.posMasterNo != null ? String(posMaster.posMasterNo) : null,
          posMasterNoSuffix: posMaster?.posMasterNoSuffix ?? null,
        },
      }));
      await BatchSavePosMasterHistoryOfficer(queryRunner, deleteOps);
      deletedCount = allToDelete.length;
    }

    // Bulk UPDATE (batch by 100 to avoid query size limits)
    if (allToUpdate.length > 0) {
      const batchSize = 100;
      for (let i = 0; i < allToUpdate.length; i += batchSize) {
        const batch = allToUpdate.slice(i, i + batchSize);
        await Promise.all(
          batch.map(({ id, data }) => queryRunner.manager.update(Position, id, data)),
        );
      }
      updatedCount = allToUpdate.length;
    }

    // Bulk INSERT
    if (allToInsert.length > 0) {
      const batchSize = 100;
      for (let i = 0; i < allToInsert.length; i += batchSize) {
        const batch = allToInsert.slice(i, i + batchSize);
        await queryRunner.manager.save(Position, batch);
      }
      insertedCount = allToInsert.length;
    }

    // Bulk UPDATE profiles
    if (profileUpdates.size > 0) {
      const profileUpdateEntries = Array.from(profileUpdates.entries());
      await Promise.all(
        profileUpdateEntries.map(([profileId, data]) =>
          queryRunner.manager.update(Profile, profileId, data),
        ),
      );
    }

    // Save PosMasterHistory for updated positions
    if (historyCalls.length > 0) {
      await BatchSavePosMasterHistoryOfficer(
        queryRunner,
        historyCalls.map(({ ancestorDNA, profileId, historyData }) => ({
          posMasterDnaId: ancestorDNA,
          profileId,
          pm: historyData,
        })),
      );
    }

    return { deleted: deletedCount, updated: updatedCount, inserted: insertedCount };
  }

  /**
   * Helper: Sum all counts from a map (for root level total)
   */
  private sumAllCounts(map: Map<string, { totalCount: number }>): number {
    let sum = 0;
    for (const value of map.values()) {
      sum += value.totalCount;
    }
    return sum;
  }

  /**
   * Helper: Sum all vacant counts from a map
   */
  private sumAllVacantCounts(
    map: Map<string, { currentVacantCount: number; nextVacantCount: number }>,
    isDraft: boolean,
  ): number {
    let sum = 0;
    for (const value of map.values()) {
      sum += isDraft ? value.nextVacantCount : value.currentVacantCount;
    }
    return sum;
  }

  /**
   * Helper: Build orgRoots children array with pre-fetched counts
   */
  private buildOrgRoots(
    orgRoots: OrgRoot[],
    positionCounts: PositionCountsByNode,
    isDraft: boolean,
  ) {
    if (!orgRoots) return [];
    return orgRoots
      .sort((a, b) => a.orgRootOrder - b.orgRootOrder)
      .map((orgRoot) => {
        const counts = getPositionCount(positionCounts.orgRootMap, orgRoot.id);
        return {
          departmentName: orgRoot.orgRootName,
          deptID: orgRoot.id,
          type: 1,
          totalPositionCount: counts.totalCount,
          totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount,
          children: this.buildOrgChild1s(orgRoot.orgChild1s, positionCounts, isDraft),
        };
      });
  }

  /**
   * Helper: Build orgChild1s children array with pre-fetched counts
   */
  private buildOrgChild1s(
    orgChild1s: OrgChild1[],
    positionCounts: PositionCountsByNode,
    isDraft: boolean,
  ) {
    if (!orgChild1s) return [];
    return orgChild1s
      .sort((a, b) => a.orgChild1Order - b.orgChild1Order)
      .map((orgChild1) => {
        const counts = getPositionCount(positionCounts.orgChild1Map, orgChild1.id);
        return {
          departmentName: orgChild1.orgChild1Name,
          deptID: orgChild1.id,
          type: 2,
          totalPositionCount: counts.totalCount,
          totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount,
          children: this.buildOrgChild2s(orgChild1.orgChild2s, positionCounts, isDraft),
        };
      });
  }

  /**
   * Helper: Build orgChild2s children array with pre-fetched counts
   */
  private buildOrgChild2s(
    orgChild2s: OrgChild2[],
    positionCounts: PositionCountsByNode,
    isDraft: boolean,
  ) {
    if (!orgChild2s) return [];
    return orgChild2s
      .sort((a, b) => a.orgChild2Order - b.orgChild2Order)
      .map((orgChild2) => {
        const counts = getPositionCount(positionCounts.orgChild2Map, orgChild2.id);
        return {
          departmentName: orgChild2.orgChild2Name,
          deptID: orgChild2.id,
          type: 3,
          totalPositionCount: counts.totalCount,
          totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount,
          children: this.buildOrgChild3s(orgChild2.orgChild3s, positionCounts, isDraft),
        };
      });
  }

  /**
   * Helper: Build orgChild3s children array with pre-fetched counts
   */
  private buildOrgChild3s(
    orgChild3s: OrgChild3[],
    positionCounts: PositionCountsByNode,
    isDraft: boolean,
  ) {
    if (!orgChild3s) return [];
    return orgChild3s
      .sort((a, b) => a.orgChild3Order - b.orgChild3Order)
      .map((orgChild3) => {
        const counts = getPositionCount(positionCounts.orgChild3Map, orgChild3.id);
        return {
          departmentName: orgChild3.orgChild3Name,
          deptID: orgChild3.id,
          type: 4,
          totalPositionCount: counts.totalCount,
          totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount,
          children: this.buildOrgChild4s(orgChild3.orgChild4s, positionCounts, isDraft),
        };
      });
  }

  /**
   * Helper: Build orgChild4s children array with pre-fetched counts (leaf nodes)
   */
  private buildOrgChild4s(
    orgChild4s: OrgChild4[],
    positionCounts: PositionCountsByNode,
    isDraft: boolean,
  ) {
    if (!orgChild4s) return [];
    return orgChild4s
      .sort((a, b) => a.orgChild4Order - b.orgChild4Order)
      .map((orgChild4) => {
        const counts = getPositionCount(positionCounts.orgChild4Map, orgChild4.id);
        return {
          departmentName: orgChild4.orgChild4Name,
          deptID: orgChild4.id,
          type: 5,
          totalPositionCount: counts.totalCount,
          totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount,
        };
      });
  }
}
