import {
  Body,
  Controller,
  Delete,
  Get,
  Patch,
  Path,
  Post,
  Query,
  Request,
  Route,
  Security,
  Tags,
} from "tsoa";
import { AppDataSource } from "../database/data-source";
import { RequestWithUser } from "../middlewares/user";
import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
import HttpSuccess from "../interfaces/http-success";
import HttpStatusCode from "../interfaces/http-status";
import { AuthRole, CreateAuthRole, CreateAddAuthRole } from "../entities/AuthRole";
import { AuthRoleAttr } from "../entities/AuthRoleAttr";
import { PosMaster } from "../entities/PosMaster";
import { EmployeePosMaster } from "../entities/EmployeePosMaster";
import { setLogDataDiff } from "../interfaces/utils";

const REDIS_HOST = process.env.REDIS_HOST;
const REDIS_PORT = process.env.REDIS_PORT;
@Route("api/v1/org/auth/authRole")
@Tags("AuthRole")
@Security("bearerAuth")
export class AuthRoleController extends Controller {
  private authRoleRepo = AppDataSource.getRepository(AuthRole);
  private authRoleAttrRepo = AppDataSource.getRepository(AuthRoleAttr);
  private posMasterRepository = AppDataSource.getRepository(PosMaster);
  private employeePosMasterRepository = AppDataSource.getRepository(EmployeePosMaster);
  private redis = require("redis");

  @Get("list")
  public async listAuthRole(
    @Request() req: RequestWithUser,
    @Query("isAdminVisibled") isAdminVisibled : string = "false",
  ) {
    let condition: any = {};
    if(isAdminVisibled.toLowerCase() === "true"){
      condition = { isAdminVisibled: true };
    }else{
      condition = {};
    }
    const getList = await this.authRoleRepo.find(
      {
        where: condition,
      }
    );
    // if (!getList || getList.length === 0) {
    //   throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
    // }
    return new HttpSuccess(getList);
  }

  @Get("{roleId}")
  public async detailAuthRole(@Path() roleId: string) {
    const getDetail = await this.authRoleRepo.findOneBy({ id: roleId });
    if (!getDetail) {
      throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
    }

    const roleAttrData = await this.authRoleAttrRepo.find({
      where: { authRoleId: getDetail.id },
    });

    const formattedData = {
      ...getDetail,
      roleAttributes: roleAttrData,
    };

    return new HttpSuccess(formattedData);
  }

  @Post()
  public async newAuthRole(@Request() req: RequestWithUser, @Body() body: CreateAuthRole) {
    const data = new AuthRole();
    const meta = {
      createdUserId: req.user.sub,
      createdFullName: req.user.name,
      lastUpdateUserId: req.user.sub,
      lastUpdateFullName: req.user.name,
      createdAt: new Date(),
      lastUpdatedAt: new Date(),
    };
    const before = null;
    Object.assign(data, { ...body, ...meta });

    await this.authRoleRepo.save(data, { data: req });
    setLogDataDiff(req, { before, after: data });
    return new HttpSuccess(data.id);
  }

  @Post("govoment")
  public async AddAuthRoleGovoment(
    @Request() req: RequestWithUser,
    @Body() body: CreateAddAuthRole,
  ) {
    let _null: any = null;
    let getDetail;

    if (body.authRoleId == "") {
      body.authRoleId = _null;
    } else {
      getDetail = await this.authRoleRepo.findOneBy({ id: body.authRoleId });
      if (!getDetail) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลสิทธิ์");
    }
    const before = null;
    const posMaster = await this.posMasterRepository.findOneBy({ id: body.posMasterId });
    if (!posMaster) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลตำแหน่ง");

    posMaster.lastUpdateUserId = req.user.sub;
    posMaster.lastUpdateFullName = req.user.name;
    posMaster.lastUpdatedAt = new Date();
    posMaster.authRoleId = body.authRoleId;
    await this.posMasterRepository.save(posMaster, { data: req });
    setLogDataDiff(req, { before, after: posMaster });

    // เช็คว่าถ้ามีค่า current_holderId ให้ลบ key สิทธิ์ใน redis
    if (posMaster.current_holderId) {
      let redisClient;
      try {
        redisClient = await this.redis.createClient({
          host: REDIS_HOST,
          port: REDIS_PORT,
        });

        redisClient.del("role_" + posMaster.current_holderId, (err: Error) => {
          if (err) console.error("Redis delete role error:", err);
        });

        redisClient.del("menu_" + posMaster.current_holderId, (err: Error) => {
          if (err) console.error("Redis delete menu error:", err);
        });
      } finally {
        if (redisClient) {
          redisClient.quit();
        }
      }
    }

    return new HttpSuccess();
  }

  @Post("employee")
  public async AddAuthRoleEmployee(
    @Request() req: RequestWithUser,
    @Body() body: CreateAddAuthRole,
  ) {
    let _null: any = null;
    let getDetail;

    if (body.authRoleId == "") {
      body.authRoleId = _null;
    } else {
      getDetail = await this.authRoleRepo.findOneBy({ id: body.authRoleId });
      if (!getDetail) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลสิทธิ์");
    }
    const before = null;
    const posMaster = await this.employeePosMasterRepository.findOneBy({ id: body.posMasterId });
    if (!posMaster) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลตำแหน่ง");

    posMaster.lastUpdateUserId = req.user.sub;
    posMaster.lastUpdateFullName = req.user.name;
    posMaster.lastUpdatedAt = new Date();
    posMaster.authRoleId = body.authRoleId;
    await this.employeePosMasterRepository.save(posMaster, { data: req });
    setLogDataDiff(req, { before, after: posMaster });
    return new HttpSuccess();
  }

  @Patch("{roleId}")
  public async editAuthRole(
    @Request() req: RequestWithUser,
    @Path() roleId: string,
    @Body()
    body: {
      roleName: string;
      roleDescription: string;
      isAdminVisibled?: boolean;
      authRoleAttrs: Array<{
        // id: string;
        authSysId: string;
        attrOwnership: string;
        attrIsCreate: boolean;
        attrIsList: boolean;
        attrIsGet: boolean;
        attrIsUpdate: boolean;
        attrIsDelete: boolean;
        attrPrivilege: string;
        parentNode: string;
      }>;
    },
  ) {
    const record = await this.authRoleRepo.findOneBy({ id: roleId });
    if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
    if (body.authRoleAttrs) {
      body.authRoleAttrs = body.authRoleAttrs.map((attr) => ({
        ...attr,
        attrOwnership: attr.attrOwnership.toUpperCase(),
        attrPrivilege: attr.attrPrivilege.toUpperCase(),
        authSysId: attr.authSysId.toUpperCase(),
        parentNode: attr.parentNode.toUpperCase(),
      }));

      Object.assign(record, {
        isAdminVisibled: body.isAdminVisibled?body.isAdminVisibled:false,
        roleName: body.roleName,
        roleDescription: body.roleDescription,
        lastUpdateFullName: req.user.name,
        lastUpdateUserId: req.user.sub,
        lastUpdatedAt: new Date(),
      });
    }
    const roleAttrData = await this.authRoleAttrRepo.find({
      where: { authRoleId: roleId },
    });

    // const updatedRoleAttrData = roleAttrData.map((attr) => {
    //   const updatedAttr = body.authRoleAttrs.find((a) => a.authSysId === attr.authSysId);
    //   if (updatedAttr) {
    //     return Object.assign(attr, updatedAttr, { lastUpdateFullName: req.user.name });
    //   }
    //   return attr;
    // });

    // const newAttrs = body.authRoleAttrs
    //   .filter((a) => !roleAttrData.some((attr) => attr.authSysId === a.authSysId))
    //   .map((attr) => {
    //     const newAttr = new AuthRoleAttr();
    //     Object.assign(newAttr, attr, {
    //       authRoleId: roleId,
    //       createdUserId: req.user.sub,
    //       createdFullName: req.user.name,
    //       lastUpdateUserId: req.user.sub,
    //             lastUpdateFullName: req.user.name,
    //   createdAt: new Date(),
    //   lastUpdatedAt: new Date(),
    //     });
    //     return newAttr;
    //   });

    // await Promise.all([
    //   this.authRoleRepo.save(record),
    //   ...updatedRoleAttrData.map((attr) => this.authRoleAttrRepo.save(attr)),
    //   ...newAttrs.map((attr) => this.authRoleAttrRepo.save(attr)),
    // ]);

    await this.authRoleAttrRepo.remove(roleAttrData, { data: req });

    const newAttrs = body.authRoleAttrs.map((attr) => {
      const newAttr = new AuthRoleAttr();
      Object.assign(newAttr, attr, {
        authRoleId: roleId,
        createdUserId: req.user.sub,
        createdFullName: req.user.name,
        lastUpdateUserId: req.user.sub,
        lastUpdateFullName: req.user.name,
        createdAt: new Date(),
        lastUpdatedAt: new Date(),
      });
      return newAttr;
    });
    const before = structuredClone(record);

    const queryRunner = AppDataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      await queryRunner.manager.save(AuthRole, record);
      await Promise.all(
        newAttrs.map((attr) => queryRunner.manager.save(AuthRoleAttr, attr))
      );
      await queryRunner.commitTransaction();

      setLogDataDiff(req, { before, after: record });
    } catch (error) {
      await queryRunner.rollbackTransaction();
      console.error("Error saving auth role:", error);
      throw new HttpError(
        HttpStatusCode.INTERNAL_SERVER_ERROR,
        "เกิดข้อผิดพลาดในการบันทึกข้อมูลบทบาท กรุณาลองใหม่ในภายหลัง"
      );
    } finally {
      await queryRunner.release();
    }

    let redisClient;
    try {
      redisClient = await this.redis.createClient({
        host: REDIS_HOST,
        port: REDIS_PORT,
      });

      await redisClient.flushdb(function (err: any, succeeded: any) {
        console.log(succeeded); // will be true if successfull
      });
    } finally {
      if (redisClient) {
        redisClient.quit();
      }
    }

    return new HttpSuccess();
  }

  @Delete("{roleId}")
  public async deleteRole(@Path() roleId: string, @Request() req: RequestWithUser) {
    let result: any;
    try {
      result = await this.authRoleRepo.findOneBy({ id: roleId });
      if (!result) {
        throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
      }
      await this.authRoleRepo.remove(result, { data: req });
    } catch {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่สามารถลบข้อมูลได้");
    }
    return new HttpSuccess();
  }
}
