import { AppDataSource } from "../database/data-source";
import { Profile } from "./../entities/Profile";
import { ProfileEmployee } from "../entities/ProfileEmployee";
import { ProfileSalary } from "./../entities/ProfileSalary";
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 { Brackets, In, Repository } from "typeorm";
import Extension from "../interfaces/extension";
import { RequestWithUser } from "../middlewares/user";

interface LeaveFilter {
  page: number;
  pageSize: number;
  searchField?: "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo";
  searchKeyword?: string;
  posType?: string;
  posLevel?: string;
  isProbation?: boolean;
  node?: number;
  nodeId?: string;
  isAll?: boolean;
  retireType?: string;
  sortBy?: string;
  sort: "ASC" | "DESC";
  _data: DataPermission;
}

interface DataPermission {
  root: string | null;
  child1: string | null;
  child2: string | null;
  child3: string | null;
  child4: string | null;
  privilege: string;
}

interface OrganizationCondition {
  condition: string;
  params: Record<string, any>;
}

interface NodeConfig {
  repository: Repository<any>;
  nameField: string;
  condition: string;
  isAllTrue: string;
  paramKey: string;
  parentIdField: string;
}
interface NodeParams {
  [key: string]: string | null | undefined;
}
interface OrgParentName {
  orgRootName: string | null;
  orgChild1Name: string | null;
  orgChild2Name: string | null;
  orgChild3Name: string | null;
  orgChild4Name: string | null;
}
export class ProfileLeaveService {
  private profileEmployeeRepo: Repository<ProfileEmployee>;
  private profileRepo: Repository<Profile>;
  private profileSalaryRepo: Repository<ProfileSalary>;
  private orgRootRepository: Repository<OrgRoot>;
  private child1Repository: Repository<OrgChild1>;
  private child2Repository: Repository<OrgChild2>;
  private child3Repository: Repository<OrgChild3>;
  private child4Repository: Repository<OrgChild4>;
  private readonly nodeConfigs: NodeConfig[];

  constructor() {
    this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee);
    this.profileRepo = AppDataSource.getRepository(Profile);
    this.profileSalaryRepo = AppDataSource.getRepository(ProfileSalary);
    this.orgRootRepository = AppDataSource.getRepository(OrgRoot);
    this.child1Repository = AppDataSource.getRepository(OrgChild1);
    this.child2Repository = AppDataSource.getRepository(OrgChild2);
    this.child3Repository = AppDataSource.getRepository(OrgChild3);
    this.child4Repository = AppDataSource.getRepository(OrgChild4);

    this.nodeConfigs = [
      {
        repository: this.orgRootRepository,
        nameField: "orgRootName",
        condition: "profileSalary.orgRoot = :orgRoot",
        isAllTrue: "profileSalary.orgChild1 IS NULL",
        paramKey: "orgRoot",
        parentIdField: "",
      },
      {
        repository: this.child1Repository,
        nameField: "orgChild1Name",
        condition: "profileSalary.orgChild1 = :orgChild1",
        isAllTrue: "profileSalary.orgChild2 IS NULL",
        paramKey: "orgChild1",
        parentIdField: "orgRootId",
      },
      {
        repository: this.child2Repository,
        nameField: "orgChild2Name",
        condition: "profileSalary.orgChild2 = :orgChild2",
        isAllTrue: "profileSalary.orgChild3 IS NULL",
        paramKey: "orgChild2",
        parentIdField: "orgChild1Id",
      },
      {
        repository: this.child3Repository,
        nameField: "orgChild3Name",
        condition: "profileSalary.orgChild3 = :orgChild3",
        isAllTrue: "profileSalary.orgChild4 IS NULL",
        paramKey: "orgChild3",
        parentIdField: "orgChild2Id",
      },
      {
        repository: this.child4Repository,
        nameField: "orgChild4Name",
        condition: "profileSalary.orgChild4 = :orgChild4",
        isAllTrue: "",
        paramKey: "orgChild4",
        parentIdField: "orgChild3Id",
      },
    ];
  }

  /** สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ */
  buildSearchQuery(searchField?: string, type: string = "profile"): string {
    switch (searchField) {
      case "citizenId":
        return `${type}.citizenId LIKE :keyword`;
      case "position":
        return `${type}.position LIKE :keyword`;
      case "posNo":
        return `
          (CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword) 
          OR (CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword)
          OR (profileSalary.posNo LIKE :keyword)
        `;
      default:
        return `CONCAT(${type}.prefix, ${type}.firstName, ' ', ${type}.lastName) LIKE :keyword`;
    }
  }

  async findOrgNodeParentAll(node: number, nodeId: string): Promise<OrgParentName> {
    const orgNames: OrgParentName = {
      orgRootName: null,
      orgChild1Name: null,
      orgChild2Name: null,
      orgChild3Name: null,
      orgChild4Name: null,
    };

    if (!nodeId || node < 0 || node >= this.nodeConfigs.length) {
      return orgNames;
    }

    let currentNode = node;
    let currentNodeId = nodeId;

    while (currentNode >= 0) {
      const config = this.nodeConfigs[currentNode];

      // Build select fields dynamically, excluding empty parentIdField
      const selectFields = [config.nameField, "id"];
      if (config.parentIdField && config.parentIdField.trim() !== "") {
        selectFields.push(config.parentIdField);
      }

      const orgData = await config.repository.findOne({
        where: { id: currentNodeId },
        select: selectFields,
      });

      if (!orgData) {
        break;
      }

      const orgName = orgData[config.nameField] || null;
      if (orgName) {
        orgNames[config.nameField as keyof OrgParentName] = orgName;
      }

      // Check if parentIdField exists and is not empty before accessing it
      if (config.parentIdField && config.parentIdField.trim() !== "") {
        currentNodeId = orgData[config.parentIdField];
        currentNode -= 1;
      } else {
        // If no parent field (root level), break the loop
        break;
      }
    }

    return orgNames;
  }

  /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */
  async buildNodeCondition(
    node: number,
    nodeId: string,
    isAll?: boolean,
  ): Promise<OrganizationCondition> {
    // Early return สำหรับ edge cases
    if (!nodeId || node < 0 || node >= this.nodeConfigs.length) {
      return { condition: "1=1", params: {} };
    }

    let nodeCondition = "";
    let params: NodeParams = {};

    const orgLists = await this.findOrgNodeParentAll(node, nodeId);

    for (let index = 0; index <= node; index++) {
      const config = this.nodeConfigs[index];
      const orgName = orgLists[config.nameField as keyof OrgParentName] || null;
      if (orgName) {
        nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition;
        nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : "";
        params[config.paramKey] = orgName;
      }
    }

    return {
      condition: nodeCondition,
      params,
    };
  }

  async getOrgNameFromId(orgIds: {
    root: string | null;
    child1: string | null;
    child2: string | null;
    child3: string | null;
    child4: string | null;
  }): Promise<OrgParentName> {
    const [rootName, child1, child2, child3, child4] = await Promise.all([
      orgIds.root
        ? this.orgRootRepository.findOne({ where: { id: orgIds.root }, select: ["orgRootName"] })
        : Promise.resolve(null),
      orgIds.child1
        ? this.child1Repository.findOne({ where: { id: orgIds.child1 }, select: ["orgChild1Name"] })
        : Promise.resolve(null),
      orgIds.child2
        ? this.child2Repository.findOne({ where: { id: orgIds.child2 }, select: ["orgChild2Name"] })
        : Promise.resolve(null),
      orgIds.child3
        ? this.child3Repository.findOne({ where: { id: orgIds.child3 }, select: ["orgChild3Name"] })
        : Promise.resolve(null),
      orgIds.child4
        ? this.child4Repository.findOne({ where: { id: orgIds.child4 }, select: ["orgChild4Name"] })
        : Promise.resolve(null),
    ]);

    return {
      orgRootName: rootName?.orgRootName ?? null,
      orgChild1Name: child1?.orgChild1Name ?? null,
      orgChild2Name: child2?.orgChild2Name ?? null,
      orgChild3Name: child3?.orgChild3Name ?? null,
      orgChild4Name: child4?.orgChild4Name ?? null,
    };
  }

  /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */
  async buildPermissionCondition(
    _data: DataPermission,
    isAll?: boolean,
  ): Promise<OrganizationCondition> {
    // Early return สำหรับ OWNER privilege
    if (_data.privilege === "OWNER" || _data.privilege === "PARENT") {
      return { condition: "1=1", params: {} };
    }

    // const nodeFields = ["root", "child1", "child2", "child3", "child4"] as const;
    let nodeCondition = "";
    let params: NodeParams = {};

    const orgLists = await this.getOrgNameFromId({
      root: _data.root,
      child1: _data.child1,
      child2: _data.child2,
      child3: _data.child3,
      child4: _data.child4,
    });
    // console.log("Org Hierarchy for Permission Condition:", orgLists);

    // check orgLists has at least one non-null value
    if (
      !orgLists.orgRootName &&
      !orgLists.orgChild1Name &&
      !orgLists.orgChild2Name &&
      !orgLists.orgChild3Name &&
      !orgLists.orgChild4Name
    ) {
      return { condition: "1=0", params: {} }; // no access
    }

    for (let index = 0; index < this.nodeConfigs.length; index++) {
      const config = this.nodeConfigs[index];
      const orgName = orgLists[config.nameField as keyof OrgParentName] || null;
      if (orgName) {
        nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition;
        nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : "";
        params[config.paramKey] = orgName;
      }
    }

    return {
      condition: nodeCondition,
      params,
    };
  }

  /** แปลงข้อมูลลูกจ้างก่อน response */
  transformEmployeeData(employee: ProfileEmployee): any {
    const dateEmployment =
      employee.profileEmployeeEmployment?.length === 0 || !employee.profileEmployeeEmployment
        ? null
        : employee.profileEmployeeEmployment.reduce((latest, current) => {
            return latest.date > current.date ? latest : current;
          }).date;

    // ตรวจสอบว่า profileSalary มีข้อมูลหรือไม่
    const salary =
      employee.profileSalary && employee.profileSalary.length > 0
        ? employee.profileSalary[0]
        : null;

    const posNo =
      salary?.posNoAbb && salary?.posNo
        ? `${salary.posNoAbb} ${salary.posNo}`
        : salary?.posNo || "";

    // สร้าง organization hierarchy - ใช้ข้อมูลจาก temp fields ถ้า salary ไม่มี
    const org = salary
      ? [salary.orgChild4, salary.orgChild3, salary.orgChild2, salary.orgChild1, salary.orgRoot]
          .filter(Boolean)
          .join("\n")
      : [
          employee.child4Temp,
          employee.child3Temp,
          employee.child2Temp,
          employee.child1Temp,
          employee.rootTemp,
        ]
          .filter(Boolean)
          .join("\n");

    // สร้าง node information
    const getNodeInfo = (nodeTemp: string) => {
      switch (nodeTemp) {
        case "0":
          return {
            name: employee.rootTemp,
            shortName: employee.rootShortNameTemp,
          };
        case "1":
          return {
            name: employee.child1Temp,
            shortName: employee.child1ShortNameTemp,
          };
        case "2":
          return {
            name: employee.child2Temp,
            shortName: employee.child2ShortNameTemp,
          };
        case "3":
          return {
            name: employee.child3Temp,
            shortName: employee.child3ShortNameTemp,
          };
        case "4":
          return {
            name: employee.child4Temp,
            shortName: employee.child4ShortNameTemp,
          };
        default:
          return { name: null, shortName: null };
      }
    };

    const nodeInfo = getNodeInfo(employee.nodeTemp || "0");

    return {
      id: employee.id,
      avatar: employee.avatar,
      avatarName: employee.avatarName,
      prefix: employee.prefix,
      rank: employee.rank,
      firstName: employee.firstName,
      lastName: employee.lastName,
      citizenId: employee.citizenId,
      posLevel: employee.posLevel?.posLevelName || null,
      posType: employee.posType?.posTypeName || null,
      posTypeShortName: employee.posType?.posTypeShortName || null,
      posLevelId: employee.posLevel?.id || null,
      posTypeId: employee.posType?.id || null,
      positionId: employee.positionIdTemp,
      posmasterId: employee.posmasterIdTemp,
      position: employee.position,
      posNo,
      employeeClass: employee.employeeClass,
      govAge: Extension.CalculateGovAge(employee.dateAppoint, 0, 0),
      age: Extension.CalculateAgeStrV2(employee.birthDate, 0, 0, "GET"),
      dateEmployment,
      dateAppoint: employee.dateAppoint,
      dateStart: employee.dateStart,
      createdAt: employee.createdAt,
      dateRetireLaw: employee.dateRetireLaw,
      draftOrganizationOrganization: nodeInfo.name,
      draftPositionEmployee: employee.positionTemp,
      draftOrgEmployeeStatus: employee.statusTemp,
      node: employee.nodeTemp,
      nodeId: employee.nodeIdTemp,
      nodeName: nodeInfo.name,
      nodeShortName: nodeInfo.shortName,
      root: employee.rootTemp || null,
      rootId: employee.rootIdTemp || null,
      rootShortName: employee.rootShortNameTemp || null,
      child1: employee.child1Temp || null,
      child1Id: employee.child1IdTemp || null,
      child1ShortName: employee.child1ShortNameTemp || null,
      child2: employee.child2Temp || null,
      child2Id: employee.child2IdTemp || null,
      child2ShortName: employee.child2ShortNameTemp || null,
      child3: employee.child3Temp || null,
      child3Id: employee.child3IdTemp || null,
      child3ShortName: employee.child3ShortNameTemp || null,
      child4: employee.child4Temp || null,
      child4Id: employee.child4IdTemp || null,
      child4ShortName: employee.child4ShortNameTemp || null,
      org,
    };
  }

  /** ค้นหาลูกจ้างที่พ้นจากราชการ */
  async getLeaveEmployees(
    request: RequestWithUser,
    filter: LeaveFilter,
  ): Promise<{ data: any[]; total: number }> {
    const {
      page,
      pageSize,
      searchField,
      searchKeyword = "",
      posType,
      posLevel,
      isProbation,
      node,
      nodeId,
      isAll,
      retireType,
      sortBy = "profileEmployee.dateLeave",
      sort,
      _data,
    } = filter;

    const t0 = Date.now();
    const searchQuery = this.buildSearchQuery(searchField, "profileEmployee");

    // สร้าง base WHERE conditions แชร์ระหว่าง count/id/data query
    const baseWhere = (qb: any) => {
      qb.where(
        new Brackets((qb2) => {
          qb2.where("profileEmployee.isLeave = :isLeave", { isLeave: true }).orWhere(
            "profileEmployee.isRetirement = :isRetirement",
            { isRetirement: true },
          );
        }),
      )
        .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" })
        .andWhere(
          new Brackets((qb2) => {
            qb2.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", {
              keyword: `%${searchKeyword}%`,
            });
          }),
        );

      if (posType) {
        qb.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` });
      }
      if (posLevel) {
        qb.andWhere(
          "CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2",
          { keyword2: `${posLevel}` },
        );
      }
      if (isProbation !== undefined && isProbation !== null) {
        qb.andWhere("profileEmployee.isProbation = :isProbation", { isProbation });
      }
      if (retireType) {
        qb.andWhere("profileEmployee.leaveType = :retireType", { retireType });
      }
    };

    // Compute permission/node conditions เพียงครั้งเดียว
    const conditions: { condition: string; params: Record<string, any> }[] = [];
    if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") {
      conditions.push(await this.buildPermissionCondition(_data, isAll));
    }
    if (node !== null && node !== undefined && nodeId) {
      conditions.push(await this.buildNodeCondition(node, nodeId, isAll));
    }
    const applyConditions = (qb: any) => {
      for (const cond of conditions) {
        qb.andWhere(cond.condition, cond.params);
      }
    };

    // console.log(`[ProfileLeaveService] getLeaveEmployees conditions took ${Date.now() - t0}ms`);

    // สร้าง salary EXISTS filter (ใช้ซ้ำทั้ง step1, step2)
    const applySalaryFilter = (qb: any) => {
      if (conditions.length > 0) {
        let existsCond = "profileSalary.positionName != :notRetire";
        const existsParams: Record<string, any> = { notRetire: "เกษียณอายุราชการ" };
        for (const cond of conditions) {
          existsCond += ` AND ${cond.condition}`;
          Object.assign(existsParams, cond.params);
        }
        qb.andWhere(
          `EXISTS (SELECT 1 FROM profileSalary WHERE profileEmployeeId = profileEmployee.id AND ${existsCond} AND profileSalary.\`order\` = (SELECT MAX(ps.\`order\`) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id AND ps.positionName != :notRetire2))`,
          { ...existsParams, notRetire2: "เกษียณอายุราชการ" }
        );
      }
    };

    // Step 1: Count query
    const countQb = this.profileEmployeeRepo
      .createQueryBuilder("profileEmployee")
      .leftJoinAndSelect("profileEmployee.posLevel", "posLevel")
      .leftJoinAndSelect("profileEmployee.posType", "posType");
    baseWhere(countQb);
    applySalaryFilter(countQb);
    const total = await countQb.getCount();

    // console.log(`[ProfileLeaveService] getLeaveEmployees count took ${Date.now() - t0}ms, total=${total}`);

    // Step 2: ดึงเฉพาะ profileEmployee IDs ที่ผ่านเงื่อนไข
    const idQb = this.profileEmployeeRepo
      .createQueryBuilder("profileEmployee")
      .select(["profileEmployee.id"])
      .leftJoin("profileEmployee.posLevel", "posLevel")
      .leftJoin("profileEmployee.posType", "posType");
    baseWhere(idQb);
    applySalaryFilter(idQb);
    idQb.orderBy(sortBy, sort).skip((page - 1) * pageSize).take(pageSize);
    const rawIds = await idQb.getRawMany();
    const employeeIds = rawIds.map((r) => r.profileEmployee_id);

    // console.log(`[ProfileLeaveService] getLeaveEmployees ids took ${Date.now() - t0}ms, ids=${employeeIds.length}`);

    if (employeeIds.length === 0) {
      return { data: [], total };
    }

    // Step 3: Load full data โดยไม่ JOIN salary
    const records = await this.profileEmployeeRepo.find({
      where: { id: In(employeeIds) },
      relations: ["posLevel", "posType", "profileEmployeeEmployment"],
      order: { [sortBy.split(".")[1]]: sort } as any,
    });

    // Step 4: Load salary เฉพาะ row ที่มี order สูงสุดต่อ profileEmployeeId (INNER JOIN + GROUP BY)
    const salaries = await this.profileSalaryRepo
      .createQueryBuilder("ps")
      .innerJoin(
        (subQuery) =>
          subQuery
            .select("ps2.profileEmployeeId", "pid")
            .addSelect("MAX(ps2.order)", "maxOrd")
            .from(ProfileSalary, "ps2")
            .where("ps2.profileEmployeeId IN (:...employeeIds)", { employeeIds })
            .andWhere("ps2.positionName != :notRetire", { notRetire: "เกษียณอายุราชการ" })
            .groupBy("ps2.profileEmployeeId"),
        "latest",
        "latest.pid = ps.profileEmployeeId AND ps.order = latest.maxOrd"
      )
      .getMany();

    // สร้าง map: profileEmployeeId → salary ที่มี order สูงสุด
    const salaryMap = new Map<string, ProfileSalary>();
    for (const s of salaries) {
      salaryMap.set(s.profileEmployeeId, s);
    }

    // แปลงข้อมูลพร้อม salary
    const data = records.map((record) => {
      const salary = salaryMap.get(record.id);
      if (salary) {
        (record as any).profileSalary = [salary];
      }
      return this.transformEmployeeData(record);
    });

    // console.log(`[ProfileLeaveService] getLeaveEmployees total took ${Date.now() - t0}ms, total=${total}`);
    return { data, total };
  }

  /**
   * แปลงข้อมูลลูกจ้างก่อน response
   */
  transformOfficerData(employee: Profile) {
    // ตรวจสอบว่า profileSalary มีข้อมูลหรือไม่
    const salary =
      employee.profileSalary && employee.profileSalary.length > 0
        ? employee.profileSalary[0]
        : null;

    const posNo =
      salary?.posNoAbb && salary?.posNo
        ? `${salary.posNoAbb} ${salary.posNo}`
        : salary?.posNo || "";

    const posExecutive = salary?.positionExecutive ? salary.positionExecutive : null;

    const root = salary?.orgRoot ? salary.orgRoot : null;

    // สร้าง organization hierarchy - ใช้ข้อมูลจาก temp fields ถ้า salary ไม่มี
    const org = salary
      ? [salary.orgChild4, salary.orgChild3, salary.orgChild2, salary.orgChild1, salary.orgRoot]
          .filter(Boolean)
          .join("\n")
      : ["", "", "", "", ""].filter(Boolean).join("\n");

    const orgRootShortName = salary?.posNoAbb ? salary.posNoAbb : null;

    return {
      id: employee.id,
      avatar: employee.avatar,
      avatarName: employee.avatarName,
      dateAppoint: employee.dateAppoint,
      prefix: employee.prefix,
      rank: employee.rank,
      firstName: employee.firstName,
      lastName: employee.lastName,
      citizenId: employee.citizenId,
      posLevel: employee.posLevel?.posLevelName || null,
      posType: employee.posType?.posTypeName || null,
      posLevelId: employee.posLevel?.id || null,
      posTypeId: employee.posType?.id || null,
      position: employee.position,
      posExecutive,
      posNo,
      rootId: null,
      root,
      orgRootShortName,
      orgRevisionId: null,
      org,
    };
  }

  /**
   * ค้นหาข้าราชการที่พ้นจากราชการ
   */
  async getLeaveOfficer(
    request: RequestWithUser,
    filter: LeaveFilter,
  ): Promise<{ data: any[]; total: number }> {
    const {
      page,
      pageSize,
      searchField,
      searchKeyword = "",
      posType,
      posLevel,
      isProbation,
      node,
      nodeId,
      isAll,
      retireType,
      sortBy = "profile.dateLeave",
      sort,
      _data,
    } = filter;

    const t0 = Date.now();
    const searchQuery = this.buildSearchQuery(searchField);

    // สร้าง base WHERE conditions แชร์ระหว่าง count/id/data query
    const baseWhere = (qb: any) => {
      qb.where(
        new Brackets((qb2) => {
          qb2.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere(
            "profile.isRetirement = :isRetirement",
            { isRetirement: true },
          );
        }),
      ).andWhere(
        new Brackets((qb2) => {
          qb2.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", {
            keyword: `%${searchKeyword}%`,
          });
        }),
      );

      if (posType) {
        qb.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` });
      }
      if (posLevel) {
        qb.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` });
      }
      if (isProbation !== undefined && isProbation !== null) {
        qb.andWhere("profile.isProbation = :isProbation", { isProbation });
      }
      if (retireType) {
        qb.andWhere("profile.leaveType = :retireType", { retireType });
      }
    };

    // Compute permission/node conditions เพียงครั้งเดียว
    const conditions: { condition: string; params: Record<string, any> }[] = [];
    if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") {
      conditions.push(await this.buildPermissionCondition(_data, isAll));
    }
    if (node !== null && node !== undefined && nodeId) {
      conditions.push(await this.buildNodeCondition(node, nodeId, isAll));
    }
    const applyConditions = (qb: any) => {
      for (const cond of conditions) {
        qb.andWhere(cond.condition, cond.params);
      }
    };

    // console.log(`[ProfileLeaveService] getLeaveOfficer conditions took ${Date.now() - t0}ms`);

    // สร้าง salary EXISTS filter (ใช้ซ้ำทั้ง step1, step2)
    const applySalaryFilter = (qb: any) => {
      if (conditions.length > 0) {
        let existsCond = "profileSalary.positionName != :notRetire";
        const existsParams: Record<string, any> = { notRetire: "เกษียณอายุราชการ" };
        for (const cond of conditions) {
          existsCond += ` AND ${cond.condition}`;
          Object.assign(existsParams, cond.params);
        }
        qb.andWhere(
          `EXISTS (SELECT 1 FROM profileSalary WHERE profileId = profile.id AND ${existsCond} AND profileSalary.\`order\` = (SELECT MAX(ps.\`order\`) FROM profileSalary ps WHERE ps.profileId = profile.id AND ps.positionName != :notRetire2))`,
          { ...existsParams, notRetire2: "เกษียณอายุราชการ" }
        );
      }
    };

    // Step 1: Count query
    const countQb = this.profileRepo
      .createQueryBuilder("profile")
      .leftJoinAndSelect("profile.posLevel", "posLevel")
      .leftJoinAndSelect("profile.posType", "posType");
    baseWhere(countQb);
    applySalaryFilter(countQb);
    const total = await countQb.getCount();

    // console.log(`[ProfileLeaveService] getLeaveOfficer count took ${Date.now() - t0}ms, total=${total}`);

    // Step 2: ดึงเฉพาะ profile IDs ที่ผ่านเงื่อนไข
    const idQb = this.profileRepo
      .createQueryBuilder("profile")
      .select(["profile.id"])
      .leftJoin("profile.posLevel", "posLevel")
      .leftJoin("profile.posType", "posType");
    baseWhere(idQb);
    applySalaryFilter(idQb);
    idQb.orderBy(sortBy, sort).skip((page - 1) * pageSize).take(pageSize);
    const rawIds = await idQb.getRawMany();
    const profileIds = rawIds.map((r) => r.profile_id);

    // console.log(`[ProfileLeaveService] getLeaveOfficer ids took ${Date.now() - t0}ms, ids=${profileIds.length}`);

    if (profileIds.length === 0) {
      return { data: [], total };
    }

    // Step 3: Load full data โดยไม่ JOIN salary
    const records = await this.profileRepo.find({
      where: { id: In(profileIds) },
      relations: ["posLevel", "posType"],
      order: { [sortBy.split(".")[1]]: sort } as any,
    });
    // console.log(`[ProfileLeaveService] getLeaveOfficer step3 (load profiles) took ${Date.now() - t0}ms`);

    // Step 4: Load salary เฉพาะ row ที่มี order สูงสุดต่อ profileId (INNER JOIN + GROUP BY)
    const salaries = await this.profileSalaryRepo
      .createQueryBuilder("ps")
      .innerJoin(
        (subQuery) =>
          subQuery
            .select("ps2.profileId", "pid")
            .addSelect("MAX(ps2.order)", "maxOrd")
            .from(ProfileSalary, "ps2")
            .where("ps2.profileId IN (:...profileIds)", { profileIds })
            .andWhere("ps2.positionName != :notRetire", { notRetire: "เกษียณอายุราชการ" })
            .groupBy("ps2.profileId"),
        "latest",
        "latest.pid = ps.profileId AND ps.order = latest.maxOrd"
      )
      .getMany();
    // console.log(`[ProfileLeaveService] getLeaveOfficer step4 (load salaries) took ${Date.now() - t0}ms, salary rows=${salaries.length}`);

    // สร้าง map: profileId → salary ที่มี order สูงสุด
    const salaryMap = new Map<string, ProfileSalary>();
    for (const s of salaries) {
      salaryMap.set(s.profileId, s);
    }

    // แปลงข้อมูลพร้อม salary
    const data = records.map((record) => {
      const salary = salaryMap.get(record.id);
      if (salary) {
        (record as any).profileSalary = [salary];
      }
      return this.transformOfficerData(record);
    });

    // console.log(`[ProfileLeaveService] getLeaveOfficer total took ${Date.now() - t0}ms, total=${total}`);
    return { data, total };
  }
}
