import { AppDataSource } from "../database/data-source";
import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Route,
  Security,
  Tags,
  Path,
  Body,
  Request,
  Example,
  Response,
} from "tsoa";
import HttpStatusCode from "../interfaces/http-status";
import HttpSuccess from "../interfaces/http-success";
import HttpError from "../interfaces/http-error";
import { In } from "typeorm";
import { OrgRevision } from "../entities/OrgRevision";
import { OrgRoot } from "../entities/OrgRoot";
import { CreateOrgChild4, OrgChild4, UpdateOrgChild4 } from "../entities/OrgChild4";
import { OrgChild3 } from "../entities/OrgChild3";
import { PosMaster } from "../entities/PosMaster";
import { Position } from "../entities/Position";
import { EmployeePosMaster } from "../entities/EmployeePosMaster";
import { EmployeePosition } from "../entities/EmployeePosition";
import { RequestWithUser } from "../middlewares/user";
import permission from "../interfaces/permission";
import { setLogDataDiff } from "../interfaces/utils";

@Route("api/v1/org/child4")
@Tags("OrgChild4")
@Security("bearerAuth")
@Response(
  HttpStatusCode.INTERNAL_SERVER_ERROR,
  "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
)
export class OrgChild4Controller extends Controller {
  private child3Repository = AppDataSource.getRepository(OrgChild3);
  private child4Repository = AppDataSource.getRepository(OrgChild4);
  private posMasterRepository = AppDataSource.getRepository(PosMaster);
  private positionRepository = AppDataSource.getRepository(Position);
  private orgRevisionRepository = AppDataSource.getRepository(OrgRevision);
  private empPosMasterRepository = AppDataSource.getRepository(EmployeePosMaster);
  private empPositionRepository = AppDataSource.getRepository(EmployeePosition);

  /**
   * API รายละเอียดโครงสร้างระดับ 4
   *
   * @summary ORG_019 - รายละเอียดโครงสร้างระดับ4 (ADMIN) #26
   *
   * @param {string} id Id Child4
   */
  @Get("{id}")
  async GetChild4(@Path() id: string) {
    const orgChild4 = await this.child4Repository.findOne({
      where: { id },
      relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3"],
    });
    if (!orgChild4) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้างระดับ 4");
    }
    const getOrgChild4 = {
      orgChild4Id: orgChild4.id,
      responsibility: orgChild4.responsibility,
      orgRootName: orgChild4.orgRoot.orgRootName,
      orgName: `${orgChild4.orgChild4Name}/${orgChild4.orgChild3.orgChild3Name}/${orgChild4.orgChild2.orgChild2Name}/${orgChild4.orgChild1.orgChild1Name}/${orgChild4.orgRoot.orgRootName}`,
      orgChild4Name: orgChild4.orgChild4Name,
      orgChild4ShortName: orgChild4.orgChild4ShortName,
      orgChild4Code: orgChild4.orgChild4Code,
      orgChild4Rank: orgChild4.orgChild4Rank,
      orgChild4RankSub: orgChild4.orgChild4RankSub,
      orgChild4Order: orgChild4.orgChild4Order,
      orgChild4PhoneEx: orgChild4.orgChild4PhoneEx,
      orgChild4PhoneIn: orgChild4.orgChild4PhoneIn,
      orgChild4Fax: orgChild4.orgChild4Fax,
      orgRevisionId: orgChild4.orgRevisionId,
      misId: orgChild4.misId,
      DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE,
      DIVISION_CODE: orgChild4.DIVISION_CODE,
      SECTION_CODE: orgChild4.SECTION_CODE,
      JOB_CODE: orgChild4.JOB_CODE,
      orgCode: orgChild4.orgRoot.orgRootCode + orgChild4.orgChild4Code,
    };
    return new HttpSuccess(getOrgChild4);
  }

  /**
   * สร้างโครงสร้างระดับ4 Child4
   *
   * @summary ORG_013 - สร้างโครงสร้างระดับ4 (ADMIN) #13
   *
   */
  @Post()
  @Example([
    {
      orgChild4Name: "string", //ชื่อหน่วยงาน
      orgChild4ShortName: "string", //อักษรย่อ
      orgChild4Code: "string", //รหัสหน่วยงาน
      // orgChild4Order: "number", //ลำดับที่ของหน่วยงาน
      orgChild4PhoneEx: "string", //หมายเลขโทรศัพท์ที่ติดต่อจากภายนอก
      orgChild4PhoneIn: "string", //หมายเลขโทรศัพท์ที่ติดต่อจากภายใน
      orgChild4Fax: "string", //หมายเลขโทรสาร
      //   orgChild4IsNormal: "boolean", //สถานะของหน่วยงาน
      orgChild3Id: "Guid", //id Child1
    },
  ])
  async create(
    @Body()
    requestBody: CreateOrgChild4,
    @Request() request: RequestWithUser,
  ) {
    await new permission().PermissionCreate(request, "SYS_ORG");
    const child3 = await this.child3Repository.findOne({
      where: { id: requestBody.orgChild3Id },
    });
    if (!child3) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found.");
    }

    const revisionIdExits = await this.orgRevisionRepository.findOne({
      where: { id: child3.orgRevisionId },
    });
    if (!revisionIdExits) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. RevisionId");
    }
    if (
      revisionIdExits.orgRevisionIsDraft != true &&
      revisionIdExits.orgRevisionIsCurrent != false
    ) {
      throw new HttpError(
        HttpStatusCode.NOT_FOUND,
        "not found. orgRevisionIsDraft:true, orgRevisionIsCurrent:false",
      );
    }

    const validOrgChild4Ranks = ["OFFICE", "DIVISION", "SECTION"];
    if (!validOrgChild4Ranks.includes(requestBody.orgChild4Rank.toUpperCase())) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. orgChild4Rank");
    }
    const order: any = await this.child4Repository.findOne({
      where: {
        orgChild3Id: requestBody.orgChild3Id,
      },
      order: { orgChild4Order: "DESC" },
    });
    const before = null;
    const child4 = Object.assign(new OrgChild4(), requestBody) as OrgChild4;
    child4.orgChild4Name = requestBody.orgChild4Name;
    child4.createdUserId = request.user.sub;
    child4.createdFullName = request.user.name;
    child4.lastUpdateUserId = request.user.sub;
    child4.lastUpdateFullName = request.user.name;
    child4.createdAt = new Date();
    child4.lastUpdatedAt = new Date();
    child4.orgRootId = String(child3?.orgRootId);
    child4.orgChild1Id = String(child3?.orgChild1Id);
    child4.orgChild2Id = String(child3?.orgChild2Id);
    child4.orgRevisionId = String(child3?.orgRevisionId);
    child4.orgChild3Id = String(child3?.id);
    child4.orgChild4Order =
      order == null || order.orgChild4Order == null ? 1 : order.orgChild4Order + 1;
    await this.child4Repository.save(child4, { data: request });
    // update ancestorDNA = id row
    child4.ancestorDNA = child4.id;
    await this.child4Repository.save(child4, { data: request });
    setLogDataDiff(request, { before, after: child4 });

    return new HttpSuccess();
  }

  /**
   * แก้ไขโครงสร้างระดับ4 Child4
   *
   * @summary ORG_014 - แก้ไขโครงสร้างระดับ4 (ADMIN) #14
   *
   * @param {string} id  Guid, *Id Child4
   */
  @Put("{id}")
  @Example([
    {
      orgChild4Name: "string", //ชื่อหน่วยงาน
      orgChild4ShortName: "string", //อักษรย่อ
      orgChild4Code: "string", //รหัสหน่วยงาน
      // orgChild4Order: "number", //ลำดับที่ของหน่วยงาน
      orgChild4PhoneEx: "string", //หมายเลขโทรศัพท์ที่ติดต่อจากภายนอก
      orgChild4PhoneIn: "string", //หมายเลขโทรศัพท์ที่ติดต่อจากภายใน
      orgChild4Fax: "string", //หมายเลขโทรสาร
      //   orgChild4IsNormal: "boolean", //สถานะของหน่วยงาน
      orgChild3Id: "Guid", //id Child1
    },
  ])
  async update(
    @Path() id: string,
    @Body()
    requestBody: UpdateOrgChild4,
    @Request() request: RequestWithUser,
  ) {
    await new permission().PermissionUpdate(request, "SYS_ORG");
    const child3IdExits = await this.child3Repository.findOne({
      where: { id: requestBody.orgChild3Id },
    });
    if (!child3IdExits) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child3Id");
    }

    const revisionIdExits = await this.orgRevisionRepository.findOne({
      where: { id: child3IdExits.orgRevisionId },
    });
    if (!revisionIdExits) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. RevisionId");
    }
    if (
      revisionIdExits.orgRevisionIsDraft != true &&
      revisionIdExits.orgRevisionIsCurrent != false
    ) {
      throw new HttpError(
        HttpStatusCode.NOT_FOUND,
        "not found. orgRevisionIsDraft:true, orgRevisionIsCurrent:false",
      );
    }

    const validOrgChild4Ranks = ["OFFICE", "DIVISION", "SECTION"];
    if (
      requestBody.orgChild4Rank == null ||
      !validOrgChild4Ranks.includes(requestBody.orgChild4Rank.toUpperCase())
    ) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. orgChild2Rank");
    }

    const child4 = await this.child4Repository.findOne({ where: { id } });
    if (!child4) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found.");
    }
    const _null: any = null;
    const before = structuredClone(child4);
    child4.lastUpdateUserId = request.user.sub;
    child4.lastUpdateFullName = request.user.name;
    child4.lastUpdatedAt = new Date();
    child4.orgRootId = String(child3IdExits?.orgRootId);
    child4.orgChild1Id = String(child3IdExits?.orgChild1Id);
    child4.orgChild2Id = String(child3IdExits?.orgChild2Id);
    child4.orgRevisionId = String(child3IdExits?.orgRevisionId);
    child4.orgChild3Id = String(child3IdExits?.id);
    this.child4Repository.merge(child4, {
      orgChild4Name: requestBody.orgChild4Name,
      orgChild4ShortName: requestBody.orgChild4ShortName,
      orgChild4Code: requestBody.orgChild4Code,
      orgChild4Rank: requestBody.orgChild4Rank != null ? requestBody.orgChild4Rank : _null,
      orgChild4RankSub: requestBody.orgChild4RankSub,
      DEPARTMENT_CODE: requestBody.DEPARTMENT_CODE != null ? requestBody.DEPARTMENT_CODE : _null,
      DIVISION_CODE: requestBody.DIVISION_CODE != null ? requestBody.DIVISION_CODE : _null,
      SECTION_CODE: requestBody.SECTION_CODE != null ? requestBody.SECTION_CODE : _null,
      JOB_CODE: requestBody.JOB_CODE != null ? requestBody.JOB_CODE : _null,
      orgChild4PhoneEx: requestBody.orgChild4PhoneEx,
      orgChild4PhoneIn: requestBody.orgChild4PhoneIn,
      orgChild4Fax: requestBody.orgChild4Fax,
      responsibility: requestBody.responsibility ?? "",
      misId: requestBody.misId,
    });
    await this.child4Repository.save(child4, { data: request });
    setLogDataDiff(request, { before, after: child4 });
    return new HttpSuccess();
  }

  /**
   * ลบโครงสร้างระดับ4 Child4
   *
   * @summary ORG_015 - ลบโครงสร้างระดับ4 (ADMIN) #15
   *
   * @param {string} id  Guid, *Id Child4
   */
  @Delete("{id}")
  async delete(@Path() id: string, @Request() request: RequestWithUser) {
    await new permission().PermissionUpdate(request, "SYS_ORG");
    const child4 = await this.child4Repository.findOne({ where: { id } });
    if (!child4) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found.");
    }

    const revisionIdExits = await this.orgRevisionRepository.findOne({
      where: { id: child4.orgRevisionId },
    });
    if (!revisionIdExits) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. RevisionId");
    }
    if (
      revisionIdExits.orgRevisionIsDraft != true &&
      revisionIdExits.orgRevisionIsCurrent != false
    ) {
      throw new HttpError(
        HttpStatusCode.NOT_FOUND,
        "not found. orgRevisionIsDraft:true, orgRevisionIsCurrent:false",
      );
    }
    const posMasters = await this.posMasterRepository.find({
      where: { orgChild4Id: id },
    });
    const positions = await this.positionRepository.find({
      where: [{ posMasterId: In(posMasters.map((x) => x.id)) }],
    });
    const empPosMasters = await this.empPosMasterRepository.find({
      where: { orgRootId: id },
    });
    const empPositions = await this.empPositionRepository.find({
      where: [{ posMasterId: In(empPosMasters.map((x) => x.id)) }],
    });

    await this.empPositionRepository.remove(empPositions, { data: request });
    await this.empPosMasterRepository.remove(empPosMasters, { data: request });
    await this.positionRepository.remove(positions, { data: request });
    await this.posMasterRepository.remove(posMasters, { data: request });
    await this.child4Repository.delete(id);
    return new HttpSuccess();
  }
}
