import {
  Body,
  Controller,
  Delete,
  Get,
  Path,
  Post,
  Put,
  Query,
  Request,
  Route,
  Security,
  Tags,
} from "tsoa";
import {
  addUserGroup,
  addUserRoles,
  createGroup,
  createUser,
  deleteGroup,
  deleteUser,
  editUser,
  getGroups,
  getRoles,
  getUser,
  getUserGroups,
  getUserList,
  removeUserGroup,
  removeUserRoles,
  getRoleMappings,
  getUserCount,
  enableStatus,
  getUserByUsername,
  changeUserPassword,
  resetPassword,
  updateUserAttributes,
} from "../keycloak";
import { AppDataSource } from "../database/data-source";
import { Profile } from "../entities/Profile";
import { ProfileEmployee } from "../entities/ProfileEmployee";
import { RequestWithUser } from "../middlewares/user";
import HttpSuccess from "../interfaces/http-success";
import { Brackets, In, IsNull, Not } from "typeorm";
import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
import { RoleKeycloak } from "../entities/RoleKeycloak";
import { addLogSequence } from "../interfaces/utils";
import { OrgRevision } from "../entities/OrgRevision";
import { Uuid } from "@elastic/elasticsearch/lib/api/types";
// import * as io from "../lib/websocket";
// import elasticsearch from "../elasticsearch";
// import { StorageFolder } from "../interfaces/storage-fs";

// if (!process.env.MINIO_BUCKET) throw Error("Default MinIO bucket must be specified.");
// if (!process.env.ELASTICSEARCH_INDEX) throw Error("Default ElasticSearch index must be specified.");

// const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
function stripLeadingSlash(str: string) {
  return str.replace(/^\//, "");
}

@Route("api/v1/org/keycloak")
@Tags("Single-Sign On")
@Security("bearerAuth")
export class KeycloakController extends Controller {
  private profileRepo = AppDataSource.getRepository(Profile);
  private profileEmpRepo = AppDataSource.getRepository(ProfileEmployee);
  private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak);

  @Get("user/{id}")
  async getUser(@Path("id") id: string) {
    const userData = await getUser(id);
    if (!userData) {
      throw new Error("User not found");
    }
    const rolesData = await getRoleMappings(id);
    if (!rolesData) {
      throw new Error("Role mappings not found");
    }
    const userDataWithRoles = {
      ...userData,
      roles: rolesData,
    };

    return userDataWithRoles;
  }

  @Post("user")
  @Security("bearerAuth", ["system", "admin"])
  async createUser(
    @Request() request: { user: { sub: string; preferred_username: string } },
    @Body()
    body: {
      username: string;
      password: string;
      firstName?: string;
      lastName?: string;
      email?: string;
      roles: string[];
      profileId?: string;
    },
  ) {
    const checkUser = await getUserByUsername(body.username);
    let userId: any = "";
    if (checkUser.length == 0) {
      userId = await createUser(body.username, body.password, {
        firstName: body.firstName,
        lastName: body.lastName,
        // email: body.email,
      });
      if (typeof userId !== "string") {
        throw new Error(userId.errorMessage);
      }
    } else {
      userId = checkUser[0].id;
    }

    const list = await getRoles();
    if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server.");
    const result = await addUserRoles(
      userId,
      list.filter((v) => body.roles.includes(v.id)),
    );

    if (!result) {
      throw new Error("Failed. Cannot set user's role.");
    }
    const profile = await this.profileRepo.findOne({
      where: {
        id: body.profileId,
      },
    });

    if (profile) {
      let _null: any = null;
      if (typeof userId === "string") {
        profile.keycloak = userId;
      }
      profile.email = body.email == null ? _null : body.email;
      profile.isDelete = false;
      await this.profileRepo.save(profile);

      // Update Keycloak with profile prefix after profile is loaded
      await updateUserAttributes(userId, {
        profileId: [profile.id],
        prefix: [profile.prefix || ""],
      });

      if (body.roles != null && body.roles.length > 0) {
        const roleKeycloak = await this.roleKeycloakRepo.find({
          where: { id: In(body.roles) },
        });
        const _profile = await this.profileRepo.findOne({
          where: { keycloak: userId },
          relations: ["roleKeycloaks"],
        });
        if (_profile) {
          _profile.roleKeycloaks = Array.from(
            new Set([..._profile.roleKeycloaks, ...roleKeycloak]),
          );
          this.profileRepo.save(_profile);
        }
      }
    }
    return userId;
  }

  @Post("user-emp")
  @Security("bearerAuth", ["system", "admin"])
  async createUserEmployee(
    @Request() request: { user: { sub: string; preferred_username: string } },
    @Body()
    body: {
      username: string;
      password: string;
      firstName?: string;
      lastName?: string;
      email?: string;
      roles: string[];
      profileId?: string;
    },
  ) {
    const userId = await createUser(body.username, body.password, {
      firstName: body.firstName,
      lastName: body.lastName,
      // email: body.email,
    });

    if (typeof userId !== "string") {
      throw new Error(userId.errorMessage);
    }
    const list = await getRoles();
    if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server.");
    const profile = await this.profileEmpRepo.findOne({
      where: {
        id: body.profileId,
      },
    });

    if (profile) {
      let _null: any = null;
      if (typeof userId === "string") {
        profile.keycloak = userId;
      }
      profile.email = body.email == null ? _null : body.email;
      profile.isDelete = false;
      await this.profileEmpRepo.save(profile);
      // Update Keycloak with profile prefix after profile is loaded
      await updateUserAttributes(userId, {
        profileId: [profile.id],
        prefix: [profile.prefix || ""],
      });

      if (body.roles != null && body.roles.length > 0) {
        const roleKeycloak = await this.roleKeycloakRepo.find({
          where: { id: In(body.roles) },
        });
        const _profile = await this.profileEmpRepo.findOne({
          where: { keycloak: userId },
          relations: ["roleKeycloaks"],
        });
        if (_profile) {
          _profile.roleKeycloaks = Array.from(
            new Set([..._profile.roleKeycloaks, ...roleKeycloak]),
          );
          this.profileEmpRepo.save(_profile);
        }
      }
    }
    return userId;
  }

  @Put("user/{userId}")
  async editUser(
    @Path() userId: string,
    @Body()
    body: {
      username?: string;
      password?: string;
      firstName?: string;
      lastName?: string;
      email?: string;
      attributes?: object;
      // roles?: string[];
    },
  ) {
    // return await editUser(userId, body);
    const chkUpdate = await editUser(userId, body);
    if (typeof chkUpdate !== "boolean") {
      throw new Error(chkUpdate.errorMessage);
    }
    // const _rolesUpdate = {
    //   role: body.roles || [],
    // };
    return chkUpdate;
  }

  @Delete("user/{userId}")
  async deleteUser(@Path() userId: string) {
    const result = await deleteUser(userId);
    if (!result) throw new Error("Failed. Cannot delete userId.");

    const profile = await this.profileRepo.findOne({
      where: {
        keycloak: userId,
      },
      relations: ["roleKeycloaks"],
    });
    if (!profile) {
      const profileEmp = await this.profileEmpRepo.findOne({
        where: {
          keycloak: userId,
          employeeClass: "PERM",
        },
        relations: ["roleKeycloaks"],
      });
      if (!profileEmp) {
      } else {
        // Task #228
        // const _null: any = null;
        // profileEmp.keycloak = _null;
        profileEmp.isDelete = true;
        profileEmp.roleKeycloaks = [];
        await this.profileEmpRepo.save(profileEmp);
      }
    } else {
      // Task #228
      // const _null: any = null;
      // profile.keycloak = _null;
      profile.isDelete = true;
      profile.roleKeycloaks = [];
      await this.profileRepo.save(profile);
      return new HttpSuccess();
    }
  }
  // @Security("bearerAuth", ["system", "admin"])

  @Get("role")
  async getRole() {
    const role = await getRoles();
    if (Array.isArray(role))
      return role.filter(
        (a) =>
          !["uma_authorization", "offline_access", "default-roles"].some((b) => a.name.includes(b)),
      );
    throw new Error("Failed. Cannot get role.");
  }

  @Post("{userId}/role")
  async addRole(@Path() userId: string, @Body() body: { role: string[] }) {
    const list = await getRoles();
    if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server.");
    const result = await addUserRoles(
      userId,
      list.filter((v) => body.role.includes(v.id)),
    );

    if (!result) {
      throw new Error("Failed. Cannot set user's role.");
    }

    const roleKeycloak = await this.roleKeycloakRepo.find({
      where: { id: In(body.role) },
    });
    const profile = await this.profileRepo.findOne({
      where: { keycloak: userId },
      relations: ["roleKeycloaks"],
    });
    if (!profile) {
      const profileEmp = await this.profileEmpRepo.findOne({
        where: { keycloak: userId, employeeClass: "PERM" },
        relations: ["roleKeycloaks"],
      });
      // if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
      if (profileEmp) {
        profileEmp.roleKeycloaks = Array.from(
          new Set([...profileEmp.roleKeycloaks, ...roleKeycloak]),
        );
        this.profileEmpRepo.save(profileEmp);
      }
    } else {
      profile.roleKeycloaks = Array.from(new Set([...profile.roleKeycloaks, ...roleKeycloak]));
      this.profileRepo.save(profile);
    }

    return new HttpSuccess();
  }

  @Delete("{userId}/role/{roleId}")
  async deleteRole(@Path() userId: string, @Path() roleId: string) {
    const profile = await this.profileRepo.findOne({
      where: { keycloak: userId },
      relations: ["roleKeycloaks"],
    });
    if (!profile) {
      const profileEmp = await this.profileEmpRepo.findOne({
        where: { keycloak: userId, employeeClass: "PERM" },
        relations: ["roleKeycloaks"],
      });
      if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
      profileEmp.roleKeycloaks = profileEmp.roleKeycloaks.filter((x) => x.id != roleId);
      this.profileEmpRepo.save(profileEmp);
    } else {
      profile.roleKeycloaks = profile.roleKeycloaks.filter((x) => x.id != roleId);
      this.profileRepo.save(profile);
    }

    const list = await getRoles();

    if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server.");

    const result = await removeUserRoles(
      userId,
      list.filter((v) => roleId === v.id),
    );
    if (!result) throw new Error("Failed. Cannot remove user's role.");
    return new HttpSuccess();
  }

  /*@Get("user")
  async getUserList(
    @Request() request: RequestWithUser,
    @Query() first = "",
    @Query() max = "",
    @Query() search = "",
  ) {
    // let _data: any = {
    //   root: null,
    //   child1: null,
    //   child2: null,
    //   child3: null,
    //   child4: null,
    // };
    // if (!request.user.role.includes("SUPER_ADMIN")) {
    //   _data = await new permission().PermissionOrgList(request, "SYS_ORG");
    // }
    // const profiles = await this.profileRepo
    //   .createQueryBuilder("profile")
    //   .leftJoinAndSelect("profile.current_holders", "current_holders")
    //   .leftJoinAndSelect("current_holders.orgRoot", "orgRoot")
    //   .leftJoinAndSelect("current_holders.orgChild1", "orgChild1")
    //   .leftJoinAndSelect("current_holders.orgChild2", "orgChild2")
    //   .leftJoinAndSelect("current_holders.orgChild3", "orgChild3")
    //   .leftJoinAndSelect("current_holders.orgChild4", "orgChild4")
    //   .andWhere(
    //     _data.root != undefined && _data.root != null
    //       ? _data.root[0] != null
    //         ? `current_holders.orgRootId IN (:...root)`
    //         : `current_holders.orgRootId is null`
    //       : "1=1",
    //     {
    //       root: _data.root,
    //     },
    //   )
    //   .andWhere(
    //     _data.child1 != undefined && _data.child1 != null
    //       ? _data.child1[0] != null
    //         ? `current_holders.orgChild1Id IN (:...child1)`
    //         : `current_holders.orgChild1Id is null`
    //       : "1=1",
    //     {
    //       child1: _data.child1,
    //     },
    //   )
    //   .andWhere(
    //     _data.child2 != undefined && _data.child2 != null
    //       ? _data.child2[0] != null
    //         ? `current_holders.orgChild2Id IN (:...child2)`
    //         : `current_holders.orgChild2Id is null`
    //       : "1=1",
    //     {
    //       child2: _data.child2,
    //     },
    //   )
    //   .andWhere(
    //     _data.child3 != undefined && _data.child3 != null
    //       ? _data.child3[0] != null
    //         ? `current_holders.orgChild3Id IN (:...child3)`
    //         : `current_holders.orgChild3Id is null`
    //       : "1=1",
    //     {
    //       child3: _data.child3,
    //     },
    //   )
    //   .andWhere(
    //     _data.child4 != undefined && _data.child4 != null
    //       ? _data.child4[0] != null
    //         ? `current_holders.orgChild4Id IN (:...child4)`
    //         : `current_holders.orgChild4Id is null`
    //       : "1=1",
    //     {
    //       child4: _data.child4,
    //     },
    //   )
    //   .andWhere({ keycloak: Not(IsNull()) })
    //   .andWhere({ keycloak: Not("") })
    //   .select("profile.keycloak", "keycloak")
    //   .getRawMany();
    // let keycloakArray = profiles.map((p) => p.keycloak);

    // const total = await getUserCountOrg(first, max, search, keycloakArray);
    // const result = await getUserListOrg(first, max, search, keycloakArray);

    const total = await getUserCount(first, max, search);
    const result = await getUserList(first, max, search);

    if (Array.isArray(result)) {
      const mappedData = await Promise.all(
        result.map(async (x) => {
          const roles = await getRoleMappings(x.id);
          return {
            id: x.id,
            username: x.username,
            firstname: x.firstName,
            lastname: x.lastName,
            email: x.email,
            roles: roles,
            enabled: x.enabled,
          };
        }),
      );

      const _mapData = {
        data: mappedData,
        total: total,
      };

      return _mapData;
    }
    throw new Error("Failed. Cannot get user list.");
  }*/

  @Post("user/admin")
  async listUserKeycloak(
    @Request() req: RequestWithUser,
    @Body()
    body: {
      page: number;
      pageSize: number;
      keyword: string | null;
      type: string;
      isAll: boolean;
      node: number | null;
      nodeId: string | null;
    },
  ) {
    let checkChildFromRole: any = {};

    if (req.user.role.includes("ADMIN") && !req.user.role.includes("SUPER_ADMIN")) {
      const profile = await this.profileRepo.findOne({
        relations: ["current_holders", "current_holders.orgRevision"],
        where: {
          keycloak: req.user.sub,
          current_holders: {
            orgRevision: {
              orgRevisionIsCurrent: true,
              orgRevisionIsDraft: false,
            },
          },
        },
      });
      //ADMIN เห็นทั้งหน่วยงานที่สังกัด
      if (profile?.current_holders[0]?.orgRootId) {
        checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}'`;
      }
    }

    let typeCondition = "";
    let checkChildConditions = "";
    let conditions: any = {};

    if (body.nodeId !== null && body.nodeId !== "" && body.nodeId !== undefined) {
      if (body.node === 0) {
        typeCondition = `current_holders.orgRootId = '${body.nodeId}'`;
        if (!body.isAll) {
          checkChildConditions = `and current_holders.orgChild1Id IS NULL`;
        }
      } else if (body.node === 1) {
        typeCondition = `current_holders.orgChild1Id = '${body.nodeId}'`;
        if (!body.isAll) {
          checkChildConditions = `and current_holders.orgChild2Id IS NULL`;
        }
      } else if (body.node === 2) {
        typeCondition = `current_holders.orgChild2Id = '${body.nodeId}'`;
        if (!body.isAll) {
          checkChildConditions = `and current_holders.orgChild3Id IS NULL`;
        }
      } else if (body.node === 3) {
        typeCondition = `current_holders.orgChild3Id = '${body.nodeId}'`;
        if (!body.isAll) {
          checkChildConditions = `and current_holders.orgChild4Id IS NULL`;
        }
      } else if (body.node === 4) {
        typeCondition = `current_holders.orgChild4Id = '${body.nodeId}'`;
      }

      conditions = typeCondition;
      if (checkChildConditions) {
        conditions += checkChildConditions;
      }
    }

    let profiles: any = [];
    let total: any;
    if (body.type.trim().toUpperCase() == "OFFICER") {
      [profiles, total] = await this.profileRepo
        .createQueryBuilder("profile")
        .leftJoinAndSelect("profile.roleKeycloaks", "roleKeycloaks")
        .leftJoinAndSelect("profile.current_holders", "current_holders")
        .leftJoinAndSelect("current_holders.orgRoot", "orgRoot")
        .leftJoinAndSelect("current_holders.orgChild1", "orgChild1")
        .leftJoinAndSelect("current_holders.orgChild2", "orgChild2")
        .leftJoinAndSelect("current_holders.orgChild3", "orgChild3")
        .leftJoinAndSelect("current_holders.orgChild4", "orgChild4")
        .where("profile.keycloak IS NOT NULL AND profile.keycloak != ''")
        .andWhere("profile.isDelete = :isDelete", { isDelete: false })
        .andWhere(checkChildFromRole)
        .andWhere(conditions)
        .andWhere(
          new Brackets((qb) => {
            qb.orWhere(
              body.keyword != null && body.keyword != ""
                ? `profile.citizenId LIKE :keyword`
                : "1=1",
                {
                  keyword: `%${body.keyword}%`,
                }
            )
              .orWhere(
                body.keyword != null && body.keyword != ""
                  ? `profile.email LIKE :keyword`
                  : "1=1",
                  {
                    keyword: `%${body.keyword}%`,
                  }
              )
              .orWhere(
                body.keyword != null && body.keyword != ""
                  ? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) LIKE :keyword`
                  : "1=1",
                  {
                    keyword: `%${body.keyword}%`,
                  }
              );
          }),
        )
        .orderBy("profile.citizenId", "ASC")
        .orderBy("orgRoot.orgRootOrder", "ASC")
        .addOrderBy("orgChild1.orgChild1Order", "ASC")
        .addOrderBy("orgChild2.orgChild2Order", "ASC")
        .addOrderBy("orgChild3.orgChild3Order", "ASC")
        .addOrderBy("orgChild4.orgChild4Order", "ASC")
        .addOrderBy("current_holders.posMasterOrder", "ASC")
        .addOrderBy("current_holders.posMasterCreatedAt", "ASC")
        .skip((body.page - 1) * body.pageSize)
        .take(body.pageSize)
        .getManyAndCount();
    } else if (body.type.trim().toUpperCase() == "EMPLOYEE") {
      [profiles, total] = await this.profileEmpRepo
        .createQueryBuilder("profileEmployee")
        .leftJoinAndSelect("profileEmployee.roleKeycloaks", "roleKeycloaks")
        .leftJoinAndSelect("profileEmployee.current_holders", "current_holders")
        .leftJoinAndSelect("current_holders.orgRoot", "orgRoot")
        .leftJoinAndSelect("current_holders.orgChild1", "orgChild1")
        .leftJoinAndSelect("current_holders.orgChild2", "orgChild2")
        .leftJoinAndSelect("current_holders.orgChild3", "orgChild3")
        .leftJoinAndSelect("current_holders.orgChild4", "orgChild4")
        .where("profileEmployee.keycloak IS NOT NULL AND profileEmployee.keycloak != ''")
        .andWhere("profileEmployee.isDelete = :isDelete", { isDelete: false })
        .andWhere(checkChildFromRole)
        .andWhere(conditions)
        .andWhere({ employeeClass: "PERM" })
        .andWhere(
          new Brackets((qb) => {
            qb.orWhere(
              body.keyword != null && body.keyword != ""
                ? `profileEmployee.citizenId LIKE :keyword`
                : "1=1",
                {
                  keyword: `%${body.keyword}%`,
                }
            )
              .orWhere(
                body.keyword != null && body.keyword != ""
                  ? `profileEmployee.email LIKE :keyword`
                  : "1=1",
                  {
                    keyword: `%${body.keyword}%`,
                  }
              )
              .orWhere(
                body.keyword != null && body.keyword != ""
                  ? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) LIKE :keyword`
                  : "1=1",
                  {
                    keyword: `%${body.keyword}%`,
                  }
              );
          }),
        )
        .orderBy("profileEmployee.citizenId", "ASC")
        .orderBy("orgRoot.orgRootOrder", "ASC")
        .addOrderBy("orgChild1.orgChild1Order", "ASC")
        .addOrderBy("orgChild2.orgChild2Order", "ASC")
        .addOrderBy("orgChild3.orgChild3Order", "ASC")
        .addOrderBy("orgChild4.orgChild4Order", "ASC")
        .addOrderBy("current_holders.posMasterOrder", "ASC")
        .addOrderBy("current_holders.posMasterCreatedAt", "ASC")
        .skip((body.page - 1) * body.pageSize)
        .take(body.pageSize)
        .getManyAndCount();
    }

    const _profiles = profiles.map((_data: any) => ({
      id: _data.keycloak,
      firstname: _data.firstName,
      lastname: _data.lastName,
      email: _data.email,
      username: _data.citizenId,
      citizenId: _data.citizenId,
      roles: _data.roleKeycloaks,
      enabled: _data.isActive,
    }));
    return new HttpSuccess({ data: _profiles, total });
  }

  @Get("group")
  async getGroup() {
    const group = await getGroups();
    if (Array.isArray(group)) return group;
    throw new Error("Failed. Cannot get group.");
  }

  @Post("group")
  async createGroup(@Body() body: { name: string }) {
    const result = await createGroup(body.name);
    if (!result) throw new Error("Failed. Cannot create group.");
  }

  @Post("log/sso")
  async addLogSSO(
    @Request() req: RequestWithUser,
    @Body()
    body: {
      text: string;
    },
  ) {
    try {
      addLogSequence(req, {
        action: "request",
        status: "success",
        description: "connected",
        request: {
          response: JSON.stringify(body.text),
        },
      });
      return new HttpSuccess();
    } catch (error) {
      addLogSequence(req, {
        action: "request",
        status: "error",
        description: "unconnected",
        request: {
          response: JSON.stringify(error),
        },
      });
      throw error;
    }
  }

  @Post("user/emp")
  @Security("bearerAuth", ["system", "admin"])
  async createUserEmp(
    @Request() request: { user: { sub: string; preferred_username: string } },
    @Body()
    body: {
      username: string;
      password: string;
      firstName?: string;
      lastName?: string;
      email?: string;
      roles: string[];
      profileId?: string;
    },
  ) {
    const checkUser = await getUserByUsername(body.username);
    let userId: any = "";
    if (checkUser.length == 0) {
      userId = await createUser(body.username, body.password, {
        firstName: body.firstName,
        lastName: body.lastName,
        // email: body.email,
      });
      if (typeof userId !== "string") {
        throw new Error(userId.errorMessage);
      }
    } else {
      userId = checkUser[0].id;
    }

    const list = await getRoles();
    if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server.");
    const result = await addUserRoles(
      userId,
      list.filter((v) => body.roles.includes(v.id)),
    );

    if (!result) {
      throw new Error("Failed. Cannot set user's role.");
    }
    const profile = await this.profileEmpRepo.findOne({
      where: {
        id: body.profileId,
      },
    });

    if (profile) {
      let _null: any = null;
      if (typeof userId === "string") {
        profile.keycloak = userId;
      }
      profile.email = body.email == null ? _null : body.email;
      profile.isDelete = false;
      await this.profileEmpRepo.save(profile);
      // Update Keycloak with profile prefix after profile is loaded
      await updateUserAttributes(userId, {
        profileId: [profile.id],
        prefix: [profile.prefix || ""],
      });

      if (body.roles != null && body.roles.length > 0) {
        const roleKeycloak = await this.roleKeycloakRepo.find({
          where: { id: In(body.roles) },
        });
        const _profile = await this.profileEmpRepo.findOne({
          where: { keycloak: userId },
          relations: ["roleKeycloaks"],
        });
        if (_profile) {
          _profile.roleKeycloaks = Array.from(
            new Set([..._profile.roleKeycloaks, ...roleKeycloak]),
          );
          this.profileEmpRepo.save(_profile);
        }
      }
    }
    return userId;
  }

  @Delete("group/{groupId}")
  async deleteGroup(@Path() groupId: string) {
    const result = await deleteGroup(groupId);
    if (!result) throw new Error("Failed. Cannot delete group.");
  }

  @Get("user/{userId}/group")
  async getUserGroup(@Path() userId: string) {
    const result = await getUserGroups(userId);
    if (!result) throw new Error("Failed. Cannot list group to user.");
    return result;
  }

  @Post("user/{userId}/group/{groupId}")
  async addUserGroup(@Path() userId: string, @Path() groupId: string) {
    const result = await addUserGroup(userId, groupId);
    if (!result) throw new Error("Failed. Cannot assign group to user.");
  }

  @Delete("user/{userId}/group/{groupId}")
  async removeUserGroup(@Path() userId: string, @Path() groupId: string) {
    const result = await removeUserGroup(userId, groupId);
    if (!result) throw new Error("Failed. Cannot remove group to user.");
  }

  @Post("user/reset-password")
  @Security("bearerAuth", ["admin"])
  async resetUserPassword(@Request() req: RequestWithUser, @Body() body: { keycloak: string }) {
    if (!req.user.role.includes("ADMIN") && !req.user.role.includes("SUPER_ADMIN")) {
      throw new HttpError(HttpStatus.FORBIDDEN, "ไม่มีสิทธิ์ดำเนินการ");
    }

    let profile: Profile | ProfileEmployee | null = await this.profileRepo.findOne({
      where: { keycloak: body.keycloak },
      select: ["id", "keycloak", "birthDate", "firstName", "lastName", "citizenId"],
    });

    let isEmployee = false;
    if (!profile) {
      profile = await this.profileEmpRepo.findOne({
        where: { keycloak: body.keycloak, employeeClass: "PERM" },
        select: ["id", "keycloak", "birthDate", "firstName", "lastName", "citizenId"],
      });
      isEmployee = true;
    }

    if (!profile) {
      throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้");
    }

    if (!profile.keycloak) {
      throw new HttpError(HttpStatus.BAD_REQUEST, "ผู้ใช้ไม่ได้เชื่อมต่อกับ Keycloak");
    }

    let newPassword: string;
    const isProduction = process.env.NODE_ENV === "production";

    if (isProduction && profile.birthDate) {
      const _date = new Date(profile.birthDate.toDateString())
        .getDate()
        .toString()
        .padStart(2, "0");
      const _month = (new Date(profile.birthDate.toDateString()).getMonth() + 1)
        .toString()
        .padStart(2, "0");
      const _year = new Date(profile.birthDate.toDateString()).getFullYear() + 543;
      newPassword = `${_date}${_month}${_year}`;
    } else {
      newPassword = "P@ssw0rd";
    }

    const result = await changeUserPassword(profile.keycloak, newPassword);
    if (!result) {
      throw new HttpError(HttpStatus.INTERNAL_SERVER_ERROR, "ไม่สามารถรีเซ็ตรหัสผ่านได้");
    }

    addLogSequence(req, {
      action: "reset-password",
      status: "success",
      description: `รีเซ็ตรหัสผ่านสำหรับ ${profile.firstName} ${profile.lastName} (${profile.citizenId})`,
    });

    const response = new HttpSuccess();
    response.message = "รีเซ็ตรหัสผ่านสำเร็จ";
    return response;
  }

  @Get("user/role/{id}")
  async getRoleUser(@Request() req: RequestWithUser, @Path("id") id: string) {
    const profile = await this.profileRepo.findOne({
      where: { keycloak: id },
      relations: ["roleKeycloaks"],
    });

    if (
      req.user.sub === id &&
      req.user.role.some((x) => x === "ADMIN") &&
      !req.user.role.some((x) => x === "SUPER_ADMIN")
    ) {
      throw new HttpError(HttpStatus.NOT_FOUND, "ไม่มีสิทธิ์เข้าถึงข้อมูลนี้");
    }

    if (!profile) {
      const profileEmp = await this.profileEmpRepo.findOne({
        where: { keycloak: id, employeeClass: "PERM" },
        relations: ["roleKeycloaks"],
      });
      if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
      return profileEmp.roleKeycloaks;
    }
    // const result = await getRoleMappings(id);
    // if (!result) {
    //   throw new Error("Role mappings not found");
    // }
    return profile.roleKeycloaks;
  }

  @Get("user/username/{citizenId}")
  async getUserByUsername(@Path("citizenId") citizenId: string) {
    const userData = await getUserByUsername(citizenId);
    if (!userData || userData.length == 0) {
      throw new Error("User not found");
    }
    const rolesData = await getRoleMappings(userData[0].id);
    if (!rolesData) {
      throw new Error("Role mappings not found");
    }
    const userDataWithRoles = {
      ...userData,
      roles: rolesData,
    };

    return userDataWithRoles;
  }

  @Put("user/{userId}/enableStatus/{status}") //#log?
  async changeEnableStatus(@Path() userId: string, @Path() status: boolean) {
    const profile = await this.profileRepo.findOne({
      where: { keycloak: userId },
    });
    if (!profile) {
      const profileEmp = await this.profileEmpRepo.findOne({
        where: { keycloak: userId, employeeClass: "PERM" },
      });
      if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
      profileEmp.isActive = status;
      this.profileEmpRepo.save(profileEmp);
    } else {
      profile.isActive = status;
      this.profileRepo.save(profile);
    }
    const result = await enableStatus(userId, status);
    if (!result) {
      throw new Error("Failed. Cannot change enable status.");
    }
    return result;
  }

  @Post("user/change-password")
  async changeUserPassword(
    @Request() request: { user: { sub: string; preferred_username: string } },
    @Body()
    body: {
      password: string;
    },
  ) {
    const result = await changeUserPassword(request.user.sub, body.password);
    if (!result) {
      throw new Error("Failed. Cannot change password.");
    }
    return result;
  }

  @Post("user/create")
  @Security("bearerAuth", ["system", "admin"])
  async createUserImport(
    @Request() request: { user: { sub: string; preferred_username: string } },
  ) {
    const profiles = await this.profileRepo.find({
      where: {
        keycloak: IsNull(),
      },
      relations: ["roleKeycloaks"],
    });

    for await (const _item of profiles) {
      let password = _item.citizenId;
      if (_item.birthDate != null) {
        // const gregorianYear = _item.birthDate.getFullYear() + 543;

        // const formattedDate =
        //   _item.birthDate.toISOString().slice(8, 10) +
        //   _item.birthDate.toISOString().slice(5, 7) +
        //   gregorianYear;
        // password = formattedDate;
        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);
      let userId: any = "";
      if (checkUser.length == 0) {
        userId = await createUser(_item.citizenId, password, {
          firstName: _item.firstName,
          lastName: _item.lastName,
          // email: _item.email,
        });
        if (typeof userId !== "string") {
          throw new Error(userId.errorMessage);
        }
      } else {
        userId = checkUser[0].id;
      }

      const list = await getRoles();
      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"),
      );

      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);
      }
    }
    return "";
  }

  @Post("user-emp/create")
  @Security("bearerAuth", ["system", "admin"])
  async createUserImportEmp(
    @Request() request: { user: { sub: string; preferred_username: string } },
  ) {
    const profiles = await this.profileEmpRepo.find({
      where: {
        // keycloak: IsNull(),
        isLeave: 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]));
              await this.profileEmpRepo.save(_item);
            }
          } catch (error) {
            console.error(`Error processing ${_item.citizenId}:`, error);
          }
        }),
      );
    }
    return "";
  }

  @Post("user/change-password-all")
  async changeUserPasswordAll(
    @Request() request: { user: { sub: string; preferred_username: string } },
  ) {
    const profiles = await this.profileRepo.find({
      where: {
        keycloak: Not(IsNull()),
      },
    });

    for await (const _item of profiles) {
      let password = _item.citizenId;
      if (_item.birthDate != null) {
        const gregorianYear = _item.birthDate.getFullYear() + 543;

        const formattedDate =
          _item.birthDate.toISOString().slice(8, 10) +
          _item.birthDate.toISOString().slice(5, 7) +
          gregorianYear;
        password = formattedDate;
      }
      const result = await changeUserPassword(_item.keycloak, password);
      if (!result) {
        continue;
      }
    }
    return;
  }

  @Post("add-role-staff/user/{child1Id}")
  @Security("bearerAuth", ["system", "admin"])
  async addroleStaffToUser(
    @Path() child1Id: string,
    @Request() request: { user: { sub: string; preferred_username: string } },
  ) {
    const profiles = await this.profileRepo.find({
      where: {
        keycloak: Not(IsNull()),
        current_holders: {
          orgChild1Id: child1Id,
        },
      },
      relations: ["roleKeycloaks"],
    });
    // return profiles.length;

    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);
      let userId: any = "";
      if (checkUser.length == 0) {
        userId = await createUser(_item.citizenId, password, {
          firstName: _item.firstName,
          lastName: _item.lastName,
        });
        if (typeof userId !== "string") {
          throw new Error(userId.errorMessage);
        }
      } else {
        userId = checkUser[0].id;
      }

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

      // if (!resultUser) {
      //   throw new Error("Failed. Cannot set user's role.");
      // }

      // if (!resultStaff) {
      //   throw new Error("Failed. Cannot set staff's role.");
      // }
      if (typeof userId === "string") {
        _item.keycloak = userId;
      }
      const roleKeycloakUser = await this.roleKeycloakRepo.find({
        where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" },
      });
      const roleKeycloakStaff = await this.roleKeycloakRepo.find({
        where: { id: "f1fff8db-0795-47c1-9952-f3c18d5b6172" },
      });
      if (_item) {
        _item.roleKeycloaks = Array.from(new Set([...roleKeycloakUser, ...roleKeycloakStaff]));
        this.profileRepo.save(_item);
      }
    }
    return "";
  }

  @Post("add-role-staff/user-emp/{child1Id}")
  @Security("bearerAuth", ["system", "admin"])
  async addroleStaffToUserEmp(
    @Path() child1Id: string,
    @Request() request: { user: { sub: string; preferred_username: string } },
  ) {
    const profiles = await this.profileEmpRepo.find({
      where: {
        keycloak: Not(IsNull()),
        isLeave: false,
        current_holders: {
          orgChild1Id: child1Id,
        },
      },
      relations: ["roleKeycloaks"],
    });
    // return profiles.length;

    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);
      let userId: any = "";
      if (checkUser.length == 0) {
        userId = await createUser(_item.citizenId, password, {
          firstName: _item.firstName,
          lastName: _item.lastName,
        });
        if (typeof userId !== "string") {
          throw new Error(userId.errorMessage);
        }
      } else {
        userId = checkUser[0].id;
      }

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

      // if (!resultUser) {
      //   throw new Error("Failed. Cannot set user's role.");
      // }
      // if (!resultStaff) {
      //   throw new Error("Failed. Cannot set staff's role.");
      // }
      if (typeof userId === "string") {
        _item.keycloak = userId;
      }
      const roleKeycloakUser = await this.roleKeycloakRepo.find({
        where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" },
      });
      const roleKeycloakStaff = await this.roleKeycloakRepo.find({
        where: { id: "f1fff8db-0795-47c1-9952-f3c18d5b6172" },
      });
      if (_item) {
        _item.roleKeycloaks = Array.from(new Set([...roleKeycloakUser, ...roleKeycloakStaff]));
        this.profileEmpRepo.save(_item);
      }
    }
    return "";
  }
}
