import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Route,
  Security,
  Tags,
  Body,
  Path,
  Request,
  Example,
  Response,
} from "tsoa";
import { AppDataSource } from "../database/data-source";
import HttpSuccess from "../interfaces/http-success";
import HttpStatusCode from "../interfaces/http-status";
import { PosType, CreatePosType, UpdatePosType } from "../entities/PosType";
import { PosLevel } from "../entities/PosLevel";
import HttpError from "../interfaces/http-error";
import { Not } from "typeorm";
import { setLogDataDiff } from "../interfaces/utils";
import { RequestWithUser } from "../middlewares/user";

@Route("api/v1/org/pos/type")
@Tags("PosType")
@Security("bearerAuth")
@Response(
  HttpStatusCode.INTERNAL_SERVER_ERROR,
  "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
)
export class PosTypeController extends Controller {
  private posTypeRepository = AppDataSource.getRepository(PosType);

  /**
   * API เพิ่มประเภทตำแหน่ง
   *
   * @summary ORG_045 - เพิ่มประเภทตำแหน่ง (ADMIN) #48
   *
   */
  @Post()
  @Example({
    positionName: "นักบริหาร",
    posTypeRank: 1,
  })
  async createType(
    @Body()
    requestBody: CreatePosType,
    @Request() request: RequestWithUser,
  ) {
    const posType = Object.assign(new PosType(), requestBody);
    if (!posType) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    }
    const chkPosTypeName = await this.posTypeRepository.findOne({
      where: {
        posTypeName: requestBody.posTypeName,
      },
    });
    if (chkPosTypeName) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ชื่อประเภทตำแหน่งนี้มีอยู่ในระบบแล้ว");
    }
    const before = null;
    posType.createdUserId = request.user.sub;
    posType.createdFullName = request.user.name;
    posType.lastUpdateUserId = request.user.sub;
    posType.lastUpdateFullName = request.user.name;
    posType.createdAt = new Date();
    posType.lastUpdatedAt = new Date();
    await this.posTypeRepository.save(posType, { data: request });
    setLogDataDiff(request, { before, after: posType });
    return new HttpSuccess(posType);
  }

  /**
   * API แก้ไขประเภทตำแหน่ง
   *
   * @summary ORG_046 - แก้ไขประเภทตำแหน่ง (ADMIN) #49
   *
   * @param {string} id  Id ประเภทตำแหน่ง
   */
  @Put("{id}")
  @Example({
    positionName: "นักบริหาร",
    posTypeRank: 1,
  })
  async editType(
    @Path() id: string,
    @Body() requestBody: UpdatePosType,
    @Request() request: RequestWithUser,
  ) {
    const posType = await this.posTypeRepository.findOne({ where: { id } });
    if (!posType) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้");
    }
    const chkPosTypeName = await this.posTypeRepository.findOne({
      where: {
        id: Not(id),
        posTypeName: requestBody.posTypeName,
      },
    });
    if (chkPosTypeName) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ชื่อประเภทตำแหน่งนี้มีอยู่ในระบบแล้ว");
    }
    const before = structuredClone(posType);
    posType.lastUpdateUserId = request.user.sub;
    posType.lastUpdateFullName = request.user.name;
    posType.lastUpdatedAt = new Date();
    this.posTypeRepository.merge(posType, requestBody);
    await this.posTypeRepository.save(posType, { data: request });
    setLogDataDiff(request, { before, after: posType });
    return new HttpSuccess(posType.id);
  }

  /**
   * API ลบประเภทตำแหน่ง
   *
   * @summary ORG_047 - ลบประเภทตำแหน่ง (ADMIN) #50
   *
   * @param {string} id  Id ตำแหน่ง
   */
  @Delete("{id}")
  async deleteType(@Path() id: string) {
    let result: any;
    try {
      result = await this.posTypeRepository.findOne({ where: { id } });
      if (!result) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้");
      }
      await this.posTypeRepository.remove(result, { data: result });
    } catch {
      throw new HttpError(
        HttpStatusCode.NOT_FOUND,
        "ไม่สามารถลบได้เนื่องจากมีการใช้งานประเภทตำแหน่งนี้อยู่",
      );
    }
    // if (result.affected == undefined || result.affected <= 0) {
    //   throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล");
    // }
    // const delPosType = await this.posTypeRepository.findOne({ where: { id } });
    // if (!delPosType) {
    //   throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้");
    // }
    // const IdExitsInLevel = await this.posLevelRepository.find({
    //   where: { posTypeId: id },
    // });
    // if (IdExitsInLevel.length > 0) {
    //   throw new HttpError(
    //     HttpStatusCode.NOT_FOUND,
    //     "ไม่สามารถลบได้เนื่องจากพบข้อมูลที่ตารางระดับตำแหน่ง",
    //   );
    // }

    // await this.posTypeRepository.remove(delPosType);
    return new HttpSuccess();
  }

  /**
   * API รายละเอียดประเภทตำแหน่ง
   *
   * @summary ORG_048 - รายละเอียดประเภทตำแหน่ง (ADMIN) #51
   *
   * @param {string} id  Id ประเภทตำแหน่ง
   */
  @Get("{id}")
  @Example([
    {
      id: "00000000-0000-0000-0000-000000000000",
      posTypeName: "นักบริหาร",
      posLevelposTypeRanks: 1,
      posLevels: [
        {
          id: "00000000-0000-0000-0000-000000000000",
          posLevelName: "นักบริหาร",
          posLevelRank: 1,
          posLevelAuthority: "HEAD",
        },
      ],
    },
  ])
  async GetTypeDetail(@Path() id: string) {
    const getPosType = await this.posTypeRepository.findOne({
      select: ["id", "posTypeName", "posTypeRank", "createdAt", "lastUpdatedAt", "lastUpdateFullName"],
      relations: ["posLevels"],
      where: { id: id },
      order: {
        posTypeRank: "ASC",
        createdAt: "DESC",
        posLevels: {
          posLevelRank: "ASC",
          createdAt: "DESC",
        }
      }
    });
    if (!getPosType) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้");
    }

    const mapGetPosType = {
      id: getPosType.id,
      posTypeName: getPosType.posTypeName,
      posTypeRank: getPosType.posTypeRank,
      posLevels: getPosType.posLevels
        .sort((a, b) => a.posLevelRank - b.posLevelRank)
        .map((posLevel) => ({
          id: posLevel.id,
          posLevelName: posLevel.posLevelName,
          posLevelRank: posLevel.posLevelRank,
          posLevelAuthority: posLevel.posLevelAuthority,
          createdAt: posLevel.createdAt,
          lastUpdatedAt: posLevel.lastUpdatedAt,
          lastUpdateFullName: posLevel.lastUpdateFullName
        })),
      createdAt: getPosType.createdAt,
      lastUpdatedAt: getPosType.lastUpdatedAt,
      lastUpdateFullName: getPosType.lastUpdateFullName
    };

    return new HttpSuccess(mapGetPosType);
  }

  /**
   * API รายการประเภทตำแหน่ง
   *
   * @summary ORG_027 - รายการประเภทตำแหน่ง (ADMIN) #29
   *
   */
  @Get()
  @Example([
    {
      id: "00000000-0000-0000-0000-000000000000",
      posTypeName: "นักบริหาร",
      posTypeRank: 1,
      posLevels: [
        {
          id: "00000000-0000-0000-0000-000000000000",
          posLevelName: "นักบริหาร",
          posLevelRank: 1,
          posLevelAuthority: "HEAD",
        },
      ],
    },
  ])
  async GetPosType() {
    const posType = await this.posTypeRepository.find({
      select: ["id", "posTypeName", "posTypeRank", "createdAt", "lastUpdatedAt", "lastUpdateFullName"],
      relations: ["posLevels"],
      order: { 
        posTypeRank: "ASC",
        createdAt: "DESC",
        posLevels: {
          posLevelRank: "ASC",
          createdAt: "DESC"
        } 
      },
    });

    const mapPosType = posType.map((item) => ({
      id: item.id,
      posTypeName: item.posTypeName,
      posTypeRank: item.posTypeRank,
      posLevels: item.posLevels
        .sort((a, b) => a.posLevelRank - b.posLevelRank)
        .map((posLevel) => ({
          id: posLevel.id,
          posLevelName: posLevel.posLevelName,
          posLevelRank: posLevel.posLevelRank,
          posLevelAuthority: posLevel.posLevelAuthority,
          createdAt: posLevel.createdAt,
          lastUpdatedAt: posLevel.lastUpdatedAt,
          lastUpdateFullName: posLevel.lastUpdateFullName,
        })),
      createdAt: item.createdAt,
      lastUpdatedAt: item.lastUpdatedAt,
      lastUpdateFullName: item.lastUpdateFullName,
    }));
    return new HttpSuccess(mapPosType);
  }
}
