import { Controller, Route, Security, Tags, Path, Request, Response, Get, Query } from "tsoa";
import { AppDataSource } from "../database/data-source";
import HttpSuccess from "../interfaces/http-success";
import HttpStatusCode from "../interfaces/http-status";
import HttpError from "../interfaces/http-error";
import { ApiName } from "../entities/ApiName";
import { isPermissionRequest } from "../middlewares/authWebService";
import { RequestWithUserWebService } from "../middlewares/user";
import { OrgRevision } from "../entities/OrgRevision";
import { ApiHistory } from "../entities/ApiHistory";
import { OrgRoot } from "../entities/OrgRoot";
import { OrgChild1 } from "../entities/OrgChild1";
import { OrgChild2 } from "../entities/OrgChild2";
import { OrgChild3 } from "../entities/OrgChild3";
import { OrgChild4 } from "../entities/OrgChild4";
import { SystemCode } from "./../interfaces/api-type";
@Route("api/v1/org/api-service")
@Tags("ApiKey")
@Security("webServiceAuth")
@Response(
  HttpStatusCode.INTERNAL_SERVER_ERROR,
  "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
)
export class ApiWebServiceController extends Controller {
  private apiNameRepository = AppDataSource.getRepository(ApiName);
  private orgRevisionRepository = AppDataSource.getRepository(OrgRevision);
  private apiHistoryRepository = AppDataSource.getRepository(ApiHistory);
  private currentRevisionId: string = "";

  // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Profile entity
  private readonly PROFILE_FIELD_REPLACEMENTS: Record<
    string,
    { propertyName: string; joinRelation: string; joinField: string }
  > = {
    posLevelName: {
      propertyName: "posLevelId",
      joinRelation: "posLevel",
      joinField: "posLevelName",
    },
    posTypeName: {
      propertyName: "posTypeId",
      joinRelation: "posType",
      joinField: "posTypeName",
    },
    registrationProvinceName: {
      propertyName: "registrationProvinceId",
      joinRelation: "registrationProvince",
      joinField: "name",
    },
    registrationDistrictName: {
      propertyName: "registrationDistrictId",
      joinRelation: "registrationDistrict",
      joinField: "name",
    },
    registrationSubDistrictName: {
      propertyName: "registrationSubDistrictId",
      joinRelation: "registrationSubDistrict",
      joinField: "name",
    },
    currentProvinceName: {
      propertyName: "currentProvinceId",
      joinRelation: "currentProvince",
      joinField: "name",
    },
    currentDistrictName: {
      propertyName: "currentDistrictId",
      joinRelation: "currentDistrict",
      joinField: "name",
    },
    currentSubDistrictName: {
      propertyName: "currentSubDistrictId",
      joinRelation: "currentSubDistrict",
      joinField: "name",
    },
  };

  // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileLeave entity
  private readonly PROFILELEAVE_FIELD_REPLACEMENTS: Record<
    string,
    { propertyName: string; joinRelation: string; joinField: string }
  > = {
    leaveTypeName: {
      propertyName: "leaveTypeId",
      joinRelation: "leaveType",
      joinField: "name",
    },
  };

  // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Position entity
  private readonly POSITION_FIELD_REPLACEMENTS: Record<
    string,
    { propertyName: string; joinRelation: string; joinField: string }
  > = {
    posTypeName: {
      propertyName: "posTypeId",
      joinRelation: "posType",
      joinField: "posTypeName",
    },
    posLevelName: {
      propertyName: "posLevelId",
      joinRelation: "posLevel",
      joinField: "posLevelName",
    },
    posExecutiveName: {
      propertyName: "posExecutiveId",
      joinRelation: "posExecutive",
      joinField: "posExecutiveName",
    },
  };

  // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileEmployee entity
  private readonly PROFILEEMPLOYEE_FIELD_REPLACEMENTS: Record<
    string,
    { propertyName: string; joinRelation: string; joinField: string }
  > = {
    posTypeName: {
      propertyName: "posTypeId",
      joinRelation: "posType",
      joinField: "posTypeName",
    },
    posLevelName: {
      propertyName: "posLevelId",
      joinRelation: "posLevel",
      joinField: "posLevelName",
    },
  };

  /**
   * build posMaster permission condition
   * @summary สร้างเงื่อนไขการกรองข้อมูลตามสิทธิ์การเข้าถึง
   */
  private buildPosMasterPermissionCondition(
    accessType: string | undefined,
    dnaIds: {
      dnaRootId?: string | null;
      dnaChild1Id?: string | null;
      dnaChild2Id?: string | null;
      dnaChild3Id?: string | null;
      dnaChild4Id?: string | null;
    },
    tableAlias: string = "posMaster",
  ): string {
    // ALL - no filtering
    if (accessType === "ALL") {
      return "1=1";
    }

    // No access type specified but has DNA IDs - default to NORMAL behavior
    const conditions: string[] = [];

    if (accessType === "ROOT" && dnaIds.dnaRootId) {
      // All organizations under this root
      conditions.push(
        `${tableAlias}.orgRootId IN (SELECT id FROM orgRoot WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%")`,
      );
    } else if (accessType === "CHILD" || accessType === "NORMAL") {
      // Build conditions based on which DNA level is specified
      if (dnaIds.dnaChild4Id) {
        conditions.push(
          `${tableAlias}.orgChild4Id IN (SELECT id FROM orgChild4 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild4Id}")`,
        );
      } else if (dnaIds.dnaChild3Id) {
        conditions.push(
          `${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild3Id}")`,
        );
        // For CHILD type, include all descendants
        if (accessType === "CHILD") {
          conditions.push(
            `(${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild3Id}%") OR ${tableAlias}.orgChild4Id IS NOT NULL)`,
          );
        }
      } else if (dnaIds.dnaChild2Id) {
        conditions.push(
          `${tableAlias}.orgChild2Id IN (SELECT id FROM orgChild2 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild2Id}")`,
        );
        if (accessType === "CHILD") {
          conditions.push(
            `(${tableAlias}.orgChild2Id IN (SELECT id FROM orgChild2 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild2Id}%") OR ${tableAlias}.orgChild3Id IS NOT NULL)`,
          );
        }
      } else if (dnaIds.dnaChild1Id) {
        conditions.push(
          `${tableAlias}.orgChild1Id IN (SELECT id FROM orgChild1 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild1Id}")`,
        );
        if (accessType === "CHILD") {
          conditions.push(
            `(${tableAlias}.orgChild1Id IN (SELECT id FROM orgChild1 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild1Id}%") OR ${tableAlias}.orgChild2Id IS NOT NULL)`,
          );
        }
      } else if (dnaIds.dnaRootId) {
        conditions.push(
          `${tableAlias}.orgRootId IN (SELECT id FROM orgRoot WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaRootId}")`,
        );
        if (accessType === "CHILD") {
          conditions.push(
            `(${tableAlias}.orgRootId IN (SELECT id FROM orgRoot WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%") OR ${tableAlias}.orgChild1Id IS NOT NULL)`,
          );
        }
      }
    }

    return conditions.length > 0 ? `(${conditions.join(" OR ")})` : "1=1";
  }

  /**
   * rename ancestorDNA to id
   * @summary เปลี่ยนชื่อฟิลด์ ancestorDNA เป็น id
   */
  private renameAncestorDnaToId(obj: any): any {
    if (!obj || typeof obj !== "object") {
      return obj;
    }
    const result = { ...obj };
    if (result.ancestorDNA !== undefined) {
      result.id = result.ancestorDNA;
      delete result.ancestorDNA;
    }
    return result;
  }

  /**
   * list fields by systems
   * @summary รายการ fields ตาม systems
   */
  @Get("/:system/:code")
  async listAttribute(
    @Request() request: RequestWithUserWebService,
    @Path("system")
    system: SystemCode,
    @Path("code") code: string,
    @Query("page") page: number = 1,
    @Query("pageSize") pageSize: number = 100,
  ): Promise<HttpSuccess | HttpError> {
    const apiName = await this.apiNameRepository.findOne({
      where: { code },
      select: ["id", "code", "methodApi", "system", "isActive"],
      relations: ["apiAttributes"],
      order: {
        apiAttributes: {
          ordering: "ASC",
        },
      },
    });

    if (!apiName || apiName.system != system || !apiName.isActive || apiName.methodApi != "GET") {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ระบุ");
    }
    await isPermissionRequest(request, apiName.id);
    const offset = (page - 1) * pageSize;
    let propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`);
    const selectedFieldsByTable: Record<string, Set<string>> = {};
    apiName.apiAttributes.forEach((attr) => {
      if (!selectedFieldsByTable[attr.tbName]) {
        selectedFieldsByTable[attr.tbName] = new Set<string>();
      }
      selectedFieldsByTable[attr.tbName].add(attr.propertyKey);
    });

    // สำหรับ Organization: ให้รวม ancestorDNA เสมอ เพื่อแสดงเป็น id
    if (system === "organization") {
      // สำหรับ OrgRoot
      const ancestorDnaField = "OrgRoot.ancestorDNA";
      if (!propertyKey.includes(ancestorDnaField)) {
        propertyKey.push(ancestorDnaField);
      }
      if (!selectedFieldsByTable["OrgRoot"]) {
        selectedFieldsByTable["OrgRoot"] = new Set<string>();
      }
      selectedFieldsByTable["OrgRoot"].add("ancestorDNA");

      // สำหรับ OrgChild1, OrgChild2, OrgChild3, OrgChild4
      const childTables = ["OrgChild1", "OrgChild2", "OrgChild3", "OrgChild4"];
      childTables.forEach((table) => {
        if (!selectedFieldsByTable[table]) {
          selectedFieldsByTable[table] = new Set<string>();
        }
        selectedFieldsByTable[table].add("ancestorDNA");
      });
    }

    let tbMain: string = "";
    let condition: string = "1=1";
    if (system == "registry") {
      tbMain = "Profile";
      condition = `Profile.isActive = true AND Profile.isDelete = false`;
    } else if (system == "registry_emp") {
      tbMain = "ProfileEmployee";
      condition = `ProfileEmployee.employeeClass = "PERM"`;
    } else if (system == "registry_temp") {
      tbMain = "ProfileEmployee";
      condition = `ProfileEmployee.employeeClass = "TEMP"`;
    } else if (system == "organization") {
      tbMain = "OrgRoot";
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });
      condition = `OrgRoot.orgRevisionId = "${revision?.id}"`;
    } else if (system == "position") {
      tbMain = "PosMaster";
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });
      condition = `PosMaster.orgRevisionId = "${revision?.id}"`;
    }

    let posMasterCondition: string = "1=1";
    let posMasterAlias: string = "";

    // Add isDeleted filtering for entities that have this field
    // Profile.ts uses isDelete (singular) instead of isDeleted
    if (tbMain === "Profile") {
      // Already handled above in the registry system condition
    } else if (
      [
        "ProfileAbility",
        "ProfileAbilityHistory",
        "ProfileAbsentLate",
        "ProfileActposition",
        "ProfileActpositionHistory",
        "ProfileAssistance",
        "ProfileAssistanceHistory",
        "ProfileAssessment",
        "ProfileAssessmentHistory",
        "ProfileCertificate",
        "ProfileCertificateHistory",
        "ProfileChangeName",
        "ProfileChangeNameHistory",
        "ProfileChildren",
        "ProfileChildrenHistory",
        "ProfileDiscipline",
        "ProfileDisciplineHistory",
        "ProfileDevelopment",
        "ProfileDevelopmentHistory",
        "ProfileDuty",
        "ProfileDutyHistory",
        "ProfileEducation",
        "ProfileEducationHistory",
        "ProfileHonor",
        "ProfileHonorHistory",
        "ProfileInsignia",
        "ProfileInsigniaHistory",
        "ProfileLeave",
        "ProfileNopaid",
        "ProfileNopaidHistory",
        "ProfileOther",
        "ProfileOtherHistory",
        "ProfileSalary",
        "ProfileSalaryHistory",
        "ProfileSalaryTemp",
        "ProfileTraining",
        "ProfileTrainingHistory",
      ].includes(tbMain)
    ) {
      condition = `${tbMain}.isDeleted = false`;
    }

    // Special handling for Profile and ProfileEmployee systems with permission filtering
    if (system == "registry") {
      // Get current revision
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });

      // Store for use in permission building
      this.currentRevisionId = revision?.id || "";
      posMasterAlias = "posMaster";

      // Build permission condition
      posMasterCondition = this.buildPosMasterPermissionCondition(
        request.user.accessType,
        {
          dnaRootId: request.user.dnaRootId,
          dnaChild1Id: request.user.dnaChild1Id,
          dnaChild2Id: request.user.dnaChild2Id,
          dnaChild3Id: request.user.dnaChild3Id,
          dnaChild4Id: request.user.dnaChild4Id,
        },
        posMasterAlias,
      );
    } else if (system == "registry_emp") {
      // Get current revision
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });

      // Store for use in permission building
      this.currentRevisionId = revision?.id || "";
      posMasterAlias = "employeePosMaster";

      // Build permission condition
      posMasterCondition = this.buildPosMasterPermissionCondition(
        request.user.accessType,
        {
          dnaRootId: request.user.dnaRootId,
          dnaChild1Id: request.user.dnaChild1Id,
          dnaChild2Id: request.user.dnaChild2Id,
          dnaChild3Id: request.user.dnaChild3Id,
          dnaChild4Id: request.user.dnaChild4Id,
        },
        posMasterAlias,
      );
    } else if (system == "registry_temp") {
      // Get current revision
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });

      // Store for use in permission building
      this.currentRevisionId = revision?.id || "";
      posMasterAlias = "employeeTempPosMaster";

      // Build permission condition
      posMasterCondition = this.buildPosMasterPermissionCondition(
        request.user.accessType,
        {
          dnaRootId: request.user.dnaRootId,
          dnaChild1Id: request.user.dnaChild1Id,
          dnaChild2Id: request.user.dnaChild2Id,
          dnaChild3Id: request.user.dnaChild3Id,
          dnaChild4Id: request.user.dnaChild4Id,
        },
        posMasterAlias,
      );
    } else if (system == "position") {
      // Get current revision
      const revision = await this.orgRevisionRepository.findOne({
        select: ["id"],
        where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
      });

      // Store for use in permission building
      this.currentRevisionId = revision?.id || "";
      posMasterAlias = "PosMaster"; // Note: Uses PascalCase to match tbMain alias

      // Build permission condition
      posMasterCondition = this.buildPosMasterPermissionCondition(
        request.user.accessType,
        {
          dnaRootId: request.user.dnaRootId,
          dnaChild1Id: request.user.dnaChild1Id,
          dnaChild2Id: request.user.dnaChild2Id,
          dnaChild3Id: request.user.dnaChild3Id,
          dnaChild4Id: request.user.dnaChild4Id,
        },
        posMasterAlias,
      );
    }

    const repo = AppDataSource.getRepository(tbMain);
    const metadata = repo.metadata;

    const relationMap: Record<string, string> = {};
    metadata.relations.forEach((rel) => {
      relationMap[rel.inverseEntityMetadata.name] = rel.propertyName;
    });

    // ดึงเฉพาะตารางรอง (ถ้าเลือกไว้)
    let propertyOtherKey: any[] = [];
    propertyOtherKey = [
      ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== tbMain)),
    ];

    // Organization hierarchy is assembled in a separate step; keep main query focused on OrgRoot only.
    if (tbMain === "OrgRoot") {
      const orgChildTables = new Set(["OrgChild1", "OrgChild2", "OrgChild3", "OrgChild4"]);
      propertyKey = propertyKey.filter((key) => !orgChildTables.has(key.split(".")[0]));
      propertyOtherKey = propertyOtherKey.filter((tb) => !orgChildTables.has(tb));
    }

    // สำหรับ Profile: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey
    const profileFieldJoins: Record<string, string> = {}; // alias -> relationName
    if (tbMain === "Profile") {
      propertyKey = propertyKey.map((key) => {
        const [table, field] = key.split(".");
        if (table === "Profile") {
          const replacement = this.PROFILE_FIELD_REPLACEMENTS[field];
          if (replacement) {
            const alias = `${table}_${replacement.joinRelation}`;
            profileFieldJoins[alias] = replacement.joinRelation;
            return `${alias}.${replacement.joinField}`;
          }
        }
        return key;
      });
    }

    // สำหรับ Position: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey
    const positionFieldJoins: Record<string, string> = {}; // alias -> relationName
    if (tbMain === "Position" || tbMain === "PosMaster") {
      propertyKey = propertyKey.map((key) => {
        const [table, field] = key.split(".");
        if (table === "Position") {
          const replacement = this.POSITION_FIELD_REPLACEMENTS[field];
          if (replacement) {
            const alias = `${table}_${replacement.joinRelation}`;
            positionFieldJoins[alias] = replacement.joinRelation;
            return `${alias}.${replacement.joinField}`;
          }
        }
        return key;
      });
    }

    // สำหรับ ProfileEmployee: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey
    const profileEmployeeFieldJoins: Record<string, string> = {}; // alias -> relationName
    if (tbMain === "ProfileEmployee") {
      propertyKey = propertyKey.map((key) => {
        const [table, field] = key.split(".");
        if (table === "ProfileEmployee") {
          const replacement = this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[field];
          if (replacement) {
            const alias = `${table}_${replacement.joinRelation}`;
            profileEmployeeFieldJoins[alias] = replacement.joinRelation;
            return `${alias}.${replacement.joinField}`;
          }
        }
        return key;
      });
    }

    // สำหรับ ProfileLeave: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey
    const profileLeaveFieldJoins: Record<string, string> = {}; // alias -> relationName
    // Process ProfileLeave fields regardless of main table
    propertyKey = propertyKey.map((key) => {
      const [table, field] = key.split(".");
      if (table === "ProfileLeave") {
        const replacement = this.PROFILELEAVE_FIELD_REPLACEMENTS[field];
        if (replacement) {
          const alias = `${table}_${replacement.joinRelation}`;
          profileLeaveFieldJoins[alias] = replacement.joinRelation;
          return `${alias}.${replacement.joinField}`;
        }
      }
      return key;
    });

    const queryBuilder = repo.createQueryBuilder(tbMain);

    // join กับตารารอง
    if (propertyOtherKey.length > 0) {
      propertyOtherKey.forEach((tb) => {
        // Skip Profile join for PosMaster - it's handled separately below
        if (tbMain === "PosMaster" && tb === "Profile") {
          return;
        }

        // Skip Position join for PosMaster - it's handled separately below
        if (tbMain === "PosMaster" && tb === "Position") {
          return;
        }

        const relationName = relationMap[tb];
        if (relationName) {
          queryBuilder.leftJoin(`${tbMain}.${relationName}`, tb);
        } else {
          // Remove fields from this table from propertyKey
          propertyKey = propertyKey.filter((key) => !key.startsWith(`${tb}.`));
        }
      });
    }

    // Check if propertyKey is empty after filtering
    if (propertyKey.length === 0) {
      throw new HttpError(
        HttpStatusCode.BAD_REQUEST,
        "ไม่พบฟิลด์ที่ต้องการแสดงผล กรุณาตรวจสอบการตั้งค่า API (ไม่สามารถ join ตารางลูกได้)",
      );
    }

    // join สำหรับฟิลด์ Profile ที่ต้องการดึงค่าจากตารางอื่น
    if (tbMain === "Profile" && Object.keys(profileFieldJoins).length > 0) {
      Object.entries(profileFieldJoins).forEach(([alias, relationName]) => {
        queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias);
      });
    }

    // join สำหรับฟิลด์ Position ที่ต้องการดึงค่าจากตารางอื่น
    if (
      (tbMain === "Position" || tbMain === "PosMaster") &&
      Object.keys(positionFieldJoins).length > 0
    ) {
      if (tbMain === "PosMaster") {
        const posMasterPositionRelation = relationMap["Position"];
        if (!posMasterPositionRelation) {
          throw new HttpError(
            HttpStatusCode.BAD_REQUEST,
            "ไม่พบความสัมพันธ์ระหว่าง PosMaster กับ Position กรุณาตรวจสอบการตั้งค่า API",
          );
        }

        // Join PosMaster -> Position once using actual relation name from metadata
        queryBuilder.leftJoin(`PosMaster.${posMasterPositionRelation}`, "Position");
      }

      Object.entries(positionFieldJoins).forEach(([alias, relationName]) => {
        if (tbMain === "PosMaster") {
          queryBuilder.leftJoin(`Position.${relationName}`, alias);
        } else {
          queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias);
        }
      });
    }

    // join สำหรับฟิลด์ ProfileEmployee ที่ต้องการดึงค่าจากตารางอื่น
    if (tbMain === "ProfileEmployee" && Object.keys(profileEmployeeFieldJoins).length > 0) {
      Object.entries(profileEmployeeFieldJoins).forEach(([alias, relationName]) => {
        queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias);
      });
    }

    // join สำหรับฟิลด์ ProfileLeave ที่ต้องการดึงค่าจากตารางอื่น
    if (Object.keys(profileLeaveFieldJoins).length > 0) {
      if (tbMain === "ProfileLeave") {
        // ProfileLeave is the main table - direct join
        Object.entries(profileLeaveFieldJoins).forEach(([alias, relationName]) => {
          queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias);
        });
      } else {
        // ProfileLeave is a related table - the base join is already created by propertyOtherKey logic
        // Join from the ProfileLeave alias (not from nested path)
        Object.entries(profileLeaveFieldJoins).forEach(([alias, relationName]) => {
          queryBuilder.leftJoin(`ProfileLeave.${relationName}`, alias);
        });
      }
    }

    // join สำหรับ PosMaster เมื่อต้องการดึงค่าจาก Profile (ข้อมูลคนครอง)
    const posMasterProfileFields: string[] = [];
    if (tbMain === "PosMaster") {
      // Collect Profile fields from both formats: "Profile.xxx" and "PosMaster.Profile.xxx"
      const extractedProfileFields = propertyKey
        .filter((key) => key.startsWith("Profile.") || key.startsWith("PosMaster.Profile."))
        .map((key) => key.replace(/^PosMaster\.Profile\./, "Profile."));

      posMasterProfileFields.push(...new Set(extractedProfileFields));

      // Remove Profile fields then add back normalized "Profile.xxx" form
      propertyKey = propertyKey.filter(
        (key) => !key.startsWith("Profile.") && !key.startsWith("PosMaster.Profile."),
      );
      propertyKey.push(...posMasterProfileFields);
    }

    // join PosMaster กับ Profile เมื่อมีการขอ Profile fields
    if (tbMain === "PosMaster" && posMasterProfileFields.length > 0) {
      // Always join via current_holder (not next_holder) because PosMaster has two relations
      // to Profile and relationMap["Profile"] would resolve to next_holder (last defined in entity)
      queryBuilder.leftJoin("PosMaster.current_holder", "Profile");
    }

    // สำหรับ registry system: เก็บ posMaster เพื่อดึง org IDs แล้วค่อย query ancestorDNA
    let includeOrgAncestorDna = false;
    if (system === "registry" || system === "registry_emp" || system === "registry_temp") {
      // Always join posMaster for registry systems (inner join to exclude profiles without current posMaster)
      // Only include posMaster from current revision
      if (tbMain === "Profile") {
        queryBuilder.innerJoin("Profile.current_holders", "posMaster", "posMaster.orgRevisionId = :currentRevisionId");
        queryBuilder.setParameter("currentRevisionId", this.currentRevisionId);

        // Add org ID fields from posMaster to propertyKey
        propertyKey.push("posMaster.orgRootId");
        propertyKey.push("posMaster.orgChild1Id");
        propertyKey.push("posMaster.orgChild2Id");
        propertyKey.push("posMaster.orgChild3Id");
        propertyKey.push("posMaster.orgChild4Id");
      } else if (tbMain === "ProfileEmployee") {
        // For registry_emp and registry_temp, also use inner join with current revision
        if (system === "registry_temp") {
          queryBuilder.innerJoin("ProfileEmployee.current_holderTemps", "employeeTempPosMaster", "employeeTempPosMaster.orgRevisionId = :currentRevisionId");
          queryBuilder.setParameter("currentRevisionId", this.currentRevisionId);

          // Add org ID fields from employeeTempPosMaster to propertyKey
          propertyKey.push("employeeTempPosMaster.orgRootId");
          propertyKey.push("employeeTempPosMaster.orgChild1Id");
          propertyKey.push("employeeTempPosMaster.orgChild2Id");
          propertyKey.push("employeeTempPosMaster.orgChild3Id");
          propertyKey.push("employeeTempPosMaster.orgChild4Id");
        } else {
          // registry_emp
          queryBuilder.innerJoin("ProfileEmployee.current_holders", "employeePosMaster", "employeePosMaster.orgRevisionId = :currentRevisionId");
          queryBuilder.setParameter("currentRevisionId", this.currentRevisionId);

          // Add org ID fields from employeePosMaster to propertyKey
          propertyKey.push("employeePosMaster.orgRootId");
          propertyKey.push("employeePosMaster.orgChild1Id");
          propertyKey.push("employeePosMaster.orgChild2Id");
          propertyKey.push("employeePosMaster.orgChild3Id");
          propertyKey.push("employeePosMaster.orgChild4Id");
        }
      }

      // Mark that we need to include ancestorDNA fields
      includeOrgAncestorDna = true;
    }

    // join กับ posMaster/employeePosMaster/employeeTempPosMaster เพื่อกรองตามสิทธิ์การเข้าถึง
    // Skip duplicate join - posMaster already joined for registry systems at lines 569-571
    // Permission condition will use the existing alias
    const posMasterAlreadyJoined = (system === "registry" || system === "registry_emp" || system === "registry_temp") && tbMain === "Profile";

    if ((tbMain === "Profile" || tbMain === "ProfileEmployee") && posMasterCondition !== "1=1") {
      if (tbMain === "Profile") {
        // Only join if not already joined for registry systems
        if (!posMasterAlreadyJoined) {
          queryBuilder.innerJoin("Profile.current_holders", "posMaster", "posMaster.orgRevisionId = :currentRevisionIdPerm");
          queryBuilder.setParameter("currentRevisionIdPerm", this.currentRevisionId);
        }
      } else if (tbMain === "ProfileEmployee") {
        // Check if already joined for registry_emp systems
        const alreadyJoined = (system === "registry_emp" || system === "registry_temp");
        // Use the correct relation based on posMasterAlias
        if (posMasterAlias === "employeeTempPosMaster" && !alreadyJoined) {
          queryBuilder.innerJoin("ProfileEmployee.current_holderTemps", "employeeTempPosMaster", "employeeTempPosMaster.orgRevisionId = :currentRevisionIdPerm");
          queryBuilder.setParameter("currentRevisionIdPerm", this.currentRevisionId);
        } else if (posMasterAlias === "employeePosMaster" && !alreadyJoined) {
          queryBuilder.innerJoin("ProfileEmployee.current_holders", "employeePosMaster", "employeePosMaster.orgRevisionId = :currentRevisionIdPerm");
          queryBuilder.setParameter("currentRevisionIdPerm", this.currentRevisionId);
        }
      }
    }

    // // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน
    // if (!propertyKey.includes(`${Main}.id`)) {
    //   propertyKey.push(`${Main}.id`);
    // }

    // add PK - ensure propertyKey is never empty
    let pk: string = "";
    const primaryColumns = metadata.primaryColumns;
    primaryColumns.forEach((col) => {
      pk = col.propertyName;
      if (!propertyKey.includes(`${tbMain}.${pk}`)) {
        propertyKey.push(`${tbMain}.${pk}`);
      }
    });

    let items: any[] = [];
    let total = 0;

    if (tbMain === "OrgRoot") {
      // Organization API should always return full hierarchy regardless of page/pageSize.
      [items, total] = await queryBuilder
        .select(propertyKey)
        .where(condition)
        .andWhere(posMasterCondition)
        .orderBy(propertyKey[0] || `${tbMain}.${pk}`, "ASC")
        .getManyAndCount();
    } else {
      [items, total] = await queryBuilder
        .select(propertyKey)
        .where(condition)
        .andWhere(posMasterCondition)
        .orderBy(propertyKey[0] || `${tbMain}.${pk}`, "ASC")
        .skip(offset)
        .take(pageSize)
        .getManyAndCount();
    }

    // สำหรับ registry systems: ดึง ancestorDNA จาก org tables
    let orgRootAncestorMap: Record<string, string> = {};
    let orgChild1AncestorMap: Record<string, string> = {};
    let orgChild2AncestorMap: Record<string, string> = {};
    let orgChild3AncestorMap: Record<string, string> = {};
    let orgChild4AncestorMap: Record<string, string> = {};

    if (includeOrgAncestorDna && items.length > 0) {
      // Collect all unique org IDs
      const orgRootIds = new Set<string>();
      const orgChild1Ids = new Set<string>();
      const orgChild2Ids = new Set<string>();
      const orgChild3Ids = new Set<string>();
      const orgChild4Ids = new Set<string>();

      items.forEach((item) => {
        // Handle Profile (registry) - current_holders
        if (item["current_holders"] && Array.isArray(item["current_holders"]) && item["current_holders"].length > 0) {
          const posMaster = item["current_holders"][0];
          if (posMaster.orgRootId) orgRootIds.add(posMaster.orgRootId);
          if (posMaster.orgChild1Id) orgChild1Ids.add(posMaster.orgChild1Id);
          if (posMaster.orgChild2Id) orgChild2Ids.add(posMaster.orgChild2Id);
          if (posMaster.orgChild3Id) orgChild3Ids.add(posMaster.orgChild3Id);
          if (posMaster.orgChild4Id) orgChild4Ids.add(posMaster.orgChild4Id);
        }
        // Handle ProfileEmployee (registry_emp) - current_holders
        if (item["current_holders"] && Array.isArray(item["current_holders"]) && item["current_holders"].length > 0) {
          const posMaster = item["current_holders"][0];
          if (posMaster.orgRootId) orgRootIds.add(posMaster.orgRootId);
          if (posMaster.orgChild1Id) orgChild1Ids.add(posMaster.orgChild1Id);
          if (posMaster.orgChild2Id) orgChild2Ids.add(posMaster.orgChild2Id);
          if (posMaster.orgChild3Id) orgChild3Ids.add(posMaster.orgChild3Id);
          if (posMaster.orgChild4Id) orgChild4Ids.add(posMaster.orgChild4Id);
        }
        // Handle ProfileEmployee (registry_temp) - current_holderTemps
        if (item["current_holderTemps"] && Array.isArray(item["current_holderTemps"]) && item["current_holderTemps"].length > 0) {
          const posMaster = item["current_holderTemps"][0];
          if (posMaster.orgRootId) orgRootIds.add(posMaster.orgRootId);
          if (posMaster.orgChild1Id) orgChild1Ids.add(posMaster.orgChild1Id);
          if (posMaster.orgChild2Id) orgChild2Ids.add(posMaster.orgChild2Id);
          if (posMaster.orgChild3Id) orgChild3Ids.add(posMaster.orgChild3Id);
          if (posMaster.orgChild4Id) orgChild4Ids.add(posMaster.orgChild4Id);
        }
      });

      // Query org tables to get ancestorDNA
      const [orgRoots, orgChild1s, orgChild2s, orgChild3s, orgChild4s] = await Promise.all([
        orgRootIds.size > 0 ? AppDataSource.getRepository(OrgRoot).createQueryBuilder("orgRoot").select(["orgRoot.id", "orgRoot.ancestorDNA"]).where("orgRoot.id IN (:...ids)", { ids: Array.from(orgRootIds) }).getMany() : [],
        orgChild1Ids.size > 0 ? AppDataSource.getRepository(OrgChild1).createQueryBuilder("orgChild1").select(["orgChild1.id", "orgChild1.ancestorDNA"]).where("orgChild1.id IN (:...ids)", { ids: Array.from(orgChild1Ids) }).getMany() : [],
        orgChild2Ids.size > 0 ? AppDataSource.getRepository(OrgChild2).createQueryBuilder("orgChild2").select(["orgChild2.id", "orgChild2.ancestorDNA"]).where("orgChild2.id IN (:...ids)", { ids: Array.from(orgChild2Ids) }).getMany() : [],
        orgChild3Ids.size > 0 ? AppDataSource.getRepository(OrgChild3).createQueryBuilder("orgChild3").select(["orgChild3.id", "orgChild3.ancestorDNA"]).where("orgChild3.id IN (:...ids)", { ids: Array.from(orgChild3Ids) }).getMany() : [],
        orgChild4Ids.size > 0 ? AppDataSource.getRepository(OrgChild4).createQueryBuilder("orgChild4").select(["orgChild4.id", "orgChild4.ancestorDNA"]).where("orgChild4.id IN (:...ids)", { ids: Array.from(orgChild4Ids) }).getMany() : [],
      ]);

      // Create separate maps for each org level
      orgRoots.forEach((org: any) => { orgRootAncestorMap[org.id] = org.ancestorDNA; });
      orgChild1s.forEach((org: any) => { orgChild1AncestorMap[org.id] = org.ancestorDNA; });
      orgChild2s.forEach((org: any) => { orgChild2AncestorMap[org.id] = org.ancestorDNA; });
      orgChild3s.forEach((org: any) => { orgChild3AncestorMap[org.id] = org.ancestorDNA; });
      orgChild4s.forEach((org: any) => { orgChild4AncestorMap[org.id] = org.ancestorDNA; });
    }

    // ลบ Main.id
    // const results = items.map(({ id, ...x }) => x);
    // const results = items.map(({ pk, ...x }) => x);

    // const results = items.map(item => {
    //   primaryColumns.forEach(col => delete item[col.propertyName]);
    //   return item;
    // });

    // split object id ออกก่อน return
    const data = items.map((item) => {
      const { [pk]: removedPk, ...rest } = item;

      // สำหรับ Profile: แปลงฟิลด์ที่มาจาก join กลับเป็นชื่อเดิม
      if (tbMain === "Profile") {
        const flattened: any = { ...rest };
        Object.entries(this.PROFILE_FIELD_REPLACEMENTS).forEach(([nameField, config]) => {
          const alias = `${tbMain}_${config.joinRelation}`;
          if (rest[alias] && rest[alias][config.joinField] !== undefined) {
            flattened[nameField] = rest[alias][config.joinField];
            delete flattened[alias];
          }
          // Also handle nested relation objects (e.g., "posLevel": { "posLevelName": "..." })
          if (
            rest[config.joinRelation] &&
            rest[config.joinRelation][config.joinField] !== undefined
          ) {
            flattened[nameField] = rest[config.joinRelation][config.joinField];
            delete flattened[config.joinRelation];
          }
        });

        // แปลง ancestorDNA เป็น orgRootId, orgChild1Id, etc.
        if (includeOrgAncestorDna) {
          if (rest["current_holders"] && Array.isArray(rest["current_holders"]) && rest["current_holders"].length > 0) {
            const posMaster = rest["current_holders"][0];

            // Get ancestorDNA from separate maps using org IDs
            // Always set the fields, use null if no value
            flattened.orgRootId = (posMaster.orgRootId && orgRootAncestorMap[posMaster.orgRootId]) ? orgRootAncestorMap[posMaster.orgRootId] : null;
            flattened.orgChild1Id = (posMaster.orgChild1Id && orgChild1AncestorMap[posMaster.orgChild1Id]) ? orgChild1AncestorMap[posMaster.orgChild1Id] : null;
            flattened.orgChild2Id = (posMaster.orgChild2Id && orgChild2AncestorMap[posMaster.orgChild2Id]) ? orgChild2AncestorMap[posMaster.orgChild2Id] : null;
            flattened.orgChild3Id = (posMaster.orgChild3Id && orgChild3AncestorMap[posMaster.orgChild3Id]) ? orgChild3AncestorMap[posMaster.orgChild3Id] : null;
            flattened.orgChild4Id = (posMaster.orgChild4Id && orgChild4AncestorMap[posMaster.orgChild4Id]) ? orgChild4AncestorMap[posMaster.orgChild4Id] : null;
          } else {
            // No current_holders, set all to null
            flattened.orgRootId = null;
            flattened.orgChild1Id = null;
            flattened.orgChild2Id = null;
            flattened.orgChild3Id = null;
            flattened.orgChild4Id = null;
          }

          // Delete current_holders array
          delete flattened["current_holders"];
        }
        return flattened;
      }

      // สำหรับ Position: แปลงฟิลด์ที่มาจาก join กลับเป็นชื่อเดิม
      if (tbMain === "Position" || tbMain === "PosMaster") {
        const flattened: any = { ...rest };
        Object.entries(this.POSITION_FIELD_REPLACEMENTS).forEach(([nameField, config]) => {
          // Remove the original ID field
          delete flattened[config.propertyName];
          // Add the name field from joined table
          const alias = `Position_${config.joinRelation}`;
          if (rest[alias] && rest[alias][config.joinField] !== undefined) {
            flattened[nameField] = rest[alias][config.joinField];
          }
          // Remove the joined table object
          delete flattened[alias];
        });
        // Remove Position object if exists
        if (flattened["Position"]) {
          delete flattened["Position"];
        }
        return flattened;
      }

      // สำหรับ ProfileEmployee: แปลงฟิลด์ที่มาจาก join กลับเป็นชื่อเดิม
      if (tbMain === "ProfileEmployee") {
        const flattened: any = { ...rest };
        Object.entries(this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS).forEach(([nameField, config]) => {
          // Remove the original ID field
          delete flattened[config.propertyName];
          // Add the name field from joined table
          const alias = `${tbMain}_${config.joinRelation}`;
          if (rest[alias] && rest[alias][config.joinField] !== undefined) {
            flattened[nameField] = rest[alias][config.joinField];
          }
          // Remove the joined table object
          delete flattened[alias];
          // Also handle nested relation objects (e.g., "posType": { "posTypeName": "..." })
          if (
            rest[config.joinRelation] &&
            rest[config.joinRelation][config.joinField] !== undefined
          ) {
            flattened[nameField] = rest[config.joinRelation][config.joinField];
            delete flattened[config.joinRelation];
          }
        });

        // แปลง ancestorDNA เป็น orgRootId, orgChild1Id, etc. (สำหรับ registry_emp และ registry_temp)
        if (includeOrgAncestorDna) {
          const posMasterKey = system === "registry_temp" ? "current_holderTemps" : "current_holders";
          if (rest[posMasterKey] && Array.isArray(rest[posMasterKey]) && rest[posMasterKey].length > 0) {
            const posMaster = rest[posMasterKey][0];

            // Always set the fields, use null if no value
            flattened.orgRootId = (posMaster.orgRootId && orgRootAncestorMap[posMaster.orgRootId]) ? orgRootAncestorMap[posMaster.orgRootId] : null;
            flattened.orgChild1Id = (posMaster.orgChild1Id && orgChild1AncestorMap[posMaster.orgChild1Id]) ? orgChild1AncestorMap[posMaster.orgChild1Id] : null;
            flattened.orgChild2Id = (posMaster.orgChild2Id && orgChild2AncestorMap[posMaster.orgChild2Id]) ? orgChild2AncestorMap[posMaster.orgChild2Id] : null;
            flattened.orgChild3Id = (posMaster.orgChild3Id && orgChild3AncestorMap[posMaster.orgChild3Id]) ? orgChild3AncestorMap[posMaster.orgChild3Id] : null;
            flattened.orgChild4Id = (posMaster.orgChild4Id && orgChild4AncestorMap[posMaster.orgChild4Id]) ? orgChild4AncestorMap[posMaster.orgChild4Id] : null;
          } else {
            // No posMaster data, set all to null
            flattened.orgRootId = null;
            flattened.orgChild1Id = null;
            flattened.orgChild2Id = null;
            flattened.orgChild3Id = null;
            flattened.orgChild4Id = null;
          }

          // Delete the posMaster array
          delete flattened[posMasterKey];
        }
        return flattened;
      }

      // สำหรับ PosMaster: แปลงฟิลด์ Profile ที่มาจาก join กลับเป็นฟิลด์ระดับบน
      if (tbMain === "PosMaster" && posMasterProfileFields.length > 0) {
        const flattened: any = { ...rest };
        const profileFieldNames = posMasterProfileFields
          .filter((field) => field.startsWith("Profile."))
          .map((field) => field.replace("Profile.", ""));

        // Extract only requested Profile fields and add top-level aliases
        if (rest["Profile"]) {
          profileFieldNames.forEach((fieldName) => {
            if (rest["Profile"][fieldName] !== undefined) {
              flattened[`profile_${fieldName}`] = rest["Profile"][fieldName];
            }
          });
          // Remove the nested Profile object
          delete flattened["Profile"];
        }
        return flattened;
      }

      // สำหรับ OrgRoot: เก็บ primary key ไว้ใช้ group ข้อมูล แล้วแยก children ภายหลัง
      if (tbMain === "OrgRoot") {
        return { __rootPk: removedPk, ...rest };
      }

      return rest;
    });

    let responseData: any[] = data;
    let responseTotal = total;

    // สำหรับ Organization: รวมข้อมูลให้เหลือ 1 root ต่อ 1 object และจัด children ตาม hierarchy
    if (tbMain === "OrgRoot") {
      const rootVisibleFields = Array.from(selectedFieldsByTable["OrgRoot"] || []);
      const child1VisibleFields = Array.from(selectedFieldsByTable["OrgChild1"] || []);
      const child2VisibleFields = Array.from(selectedFieldsByTable["OrgChild2"] || []);
      const child3VisibleFields = Array.from(selectedFieldsByTable["OrgChild3"] || []);
      const child4VisibleFields = Array.from(selectedFieldsByTable["OrgChild4"] || []);

      const pickVisibleFields = (obj: any, fields: string[]) => {
        const out: any = {};
        fields.forEach((field) => {
          if (obj[field] !== undefined) {
            // ถ้าเป็น ancestorDNA ให้เปลี่ยนชื่อเป็น id
            if (field === "ancestorDNA") {
              out.id = obj[field];
            } else {
              out[field] = obj[field];
            }
          }
        });
        return out;
      };

      const rootMap = new Map<string, any>();
      data.forEach((row: any) => {
        if (!row.__rootPk || rootMap.has(row.__rootPk)) {
          return;
        }

        const rootNode = {
          ...pickVisibleFields(row, rootVisibleFields),
          children: [],
        };
        rootMap.set(row.__rootPk, rootNode);
      });

      const rootIds = Array.from(rootMap.keys());

      if (rootIds.length > 0) {
        const buildSelect = (alias: string, required: string[], visible: string[]) =>
          Array.from(new Set([...required, ...visible])).map((field) => `${alias}.${field}`);

        const [child1Rows, child2Rows, child3Rows, child4Rows] = await Promise.all([
          AppDataSource.getRepository(OrgChild1)
            .createQueryBuilder("OrgChild1")
            .select(buildSelect("OrgChild1", ["id", "orgRootId", "ancestorDNA"], child1VisibleFields))
            .where("OrgChild1.orgRootId IN (:...rootIds)", { rootIds })
            .orderBy("OrgChild1.id", "ASC")
            .getMany(),
          AppDataSource.getRepository(OrgChild2)
            .createQueryBuilder("OrgChild2")
            .select(
              buildSelect("OrgChild2", ["id", "orgRootId", "orgChild1Id", "ancestorDNA"], child2VisibleFields),
            )
            .where("OrgChild2.orgRootId IN (:...rootIds)", { rootIds })
            .orderBy("OrgChild2.id", "ASC")
            .getMany(),
          AppDataSource.getRepository(OrgChild3)
            .createQueryBuilder("OrgChild3")
            .select(
              buildSelect(
                "OrgChild3",
                ["id", "orgRootId", "orgChild1Id", "orgChild2Id", "ancestorDNA"],
                child3VisibleFields,
              ),
            )
            .where("OrgChild3.orgRootId IN (:...rootIds)", { rootIds })
            .orderBy("OrgChild3.id", "ASC")
            .getMany(),
          AppDataSource.getRepository(OrgChild4)
            .createQueryBuilder("OrgChild4")
            .select(
              buildSelect(
                "OrgChild4",
                ["id", "orgRootId", "orgChild1Id", "orgChild2Id", "orgChild3Id", "ancestorDNA"],
                child4VisibleFields,
              ),
            )
            .where("OrgChild4.orgRootId IN (:...rootIds)", { rootIds })
            .orderBy("OrgChild4.id", "ASC")
            .getMany(),
        ]);

        const child1Map = new Map<string, any>();
        const child2Map = new Map<string, any>();
        const child3Map = new Map<string, any>();

        child1Rows.forEach((row) => {
          const node = {
            ...pickVisibleFields(row, child1VisibleFields),
            children: [],
          };
          child1Map.set(row.id, node);

          const rootNode = rootMap.get(row.orgRootId);
          if (rootNode) {
            rootNode.children.push(node);
          }
        });

        child2Rows.forEach((row) => {
          const node = {
            ...pickVisibleFields(row, child2VisibleFields),
            children: [],
          };
          child2Map.set(row.id, node);

          const parent = child1Map.get(row.orgChild1Id);
          if (parent) {
            parent.children.push(node);
          }
        });

        child3Rows.forEach((row) => {
          const node = {
            ...pickVisibleFields(row, child3VisibleFields),
            children: [],
          };
          child3Map.set(row.id, node);

          const parent = child2Map.get(row.orgChild2Id);
          if (parent) {
            parent.children.push(node);
          }
        });

        child4Rows.forEach((row) => {
          const node = {
            ...pickVisibleFields(row, child4VisibleFields),
          };

          const parent = child3Map.get(row.orgChild3Id);
          if (parent) {
            if (!Array.isArray(parent.children)) {
              parent.children = [];
            }
            parent.children.push(node);
          }
        });
      }

      responseData = Array.from(rootMap.values());

      // สำหรับ Organization: เปลี่ยนชื่อ ancestorDNA เป็น id
      responseData = responseData.map((root: any) => {
        const renamed = this.renameAncestorDnaToId(root);
        if (Array.isArray(renamed.children)) {
          renamed.children = renamed.children.map((child1: any) => {
            const renamedChild1 = this.renameAncestorDnaToId(child1);
            if (Array.isArray(renamedChild1.children)) {
              renamedChild1.children = renamedChild1.children.map((child2: any) => {
                const renamedChild2 = this.renameAncestorDnaToId(child2);
                if (Array.isArray(renamedChild2.children)) {
                  renamedChild2.children = renamedChild2.children.map((child3: any) => {
                    const renamedChild3 = this.renameAncestorDnaToId(child3);
                    if (Array.isArray(renamedChild3.children)) {
                      renamedChild3.children = renamedChild3.children.map((child4: any) =>
                        this.renameAncestorDnaToId(child4),
                      );
                    }
                    return renamedChild3;
                  });
                }
                return renamedChild2;
              });
            }
            return renamedChild1;
          });
        }
        return renamed;
      });

      responseTotal = responseData.length;
    }

    // save api history after query success
    const history = {
      headerApi: JSON.stringify({
        host: request.headers.host,
        "x-api-key": request.headers["x-api-key"],
        connection: request.headers.connection,
        accept: request.headers.accept,
      }),
      tokenApi: Array.isArray(request.headers["x-api-key"])
        ? request.headers["x-api-key"][0] || ""
        : request.headers["x-api-key"] || "",
      requestApi: `${request.method} ${request.protocol}://${request.headers.host}${request.originalUrl || request.url}`,
      responseApi: "OK",
      ipApi: request.ip,
      codeApi: code,
      apiKeyId: request.user.id,
      apiNameId: apiName.id,
      createdFullName: request.user.name,
      lastUpdateFullName: request.user.name,
    };
    await this.apiHistoryRepository.save(history);

    // const results = data.map((item) => {
    //   const flattenedItem: any = {};

    //   // Extract nested object properties to top level
    //   Object.keys(item).forEach((key) => {
    //     const value = item[key];
    //     if (value && typeof value === "object") {
    //       // if (Array.isArray(value) && value.length === 1) {
    //       //   // If array has single item, extract it as object
    //       //   Object.assign(flattenedItem, value[0]);
    //       // } else
    //       if (!Array.isArray(value)) {
    //         // Merge nested object properties to top level
    //         Object.assign(flattenedItem, value);
    //       } else {
    //         // Keep arrays with multiple items or empty arrays as is
    //         flattenedItem[key] = value;
    //       }
    //     } else {
    //       // Keep primitive values as is
    //       flattenedItem[key] = value;
    //     }
    //   });

    //   return flattenedItem;
    // });
    return new HttpSuccess({ data: responseData, total: responseTotal });
  }
}
