import {
  Controller,
  Post,
  Delete,
  Route,
  Security,
  Tags,
  Body,
  Path,
  Request,
  Response,
  Get,
} from "tsoa";
import { LessThan, MoreThan } from "typeorm";
import { AppDataSource } from "../database/data-source";
import HttpSuccess from "../interfaces/http-success";
import HttpStatusCode from "../interfaces/http-status";
import HttpError from "../interfaces/http-error";
import { Command } from "../entities/Command";
import { CommandOperator, CreateCommandOperatorDto } from "../entities/CommandOperator";
import { RequestWithUser } from "../middlewares/user";

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

  /**
   * API รายชื่อเจ้าหน้าที่ดำเนินการที่คำสั่ง
   * @summary API รายชื่อเจ้าหน้าที่ดำเนินการที่คำสั่ง
   * @param commandId คีย์คำสั่ง
   */
  @Get("{commandId}")
  async getCommandOperatorByCommandId(@Path() commandId: string) {
    const command = await this.commandRepo.findOne({
      where: { id: commandId },
      select: { id: true },
    });
    if (!command) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้");
    }
    const commandOperators = await this.commandOperatorRepo.find({
      where: { commandId: command.id },
      order: { orderNo: "ASC" },
    });
    return new HttpSuccess(commandOperators);
  }

  /**
   * API สลับลำดับเจ้าหน้าที่ดำเนินการ (UP / DOWN)
   * @summary API สลับลำดับเจ้าหน้าที่ดำเนินการ (UP / DOWN)
   * @param direction สลับขึ้นหรือลง (UP / DOWN)
   * @param operatorId คีย์เจ้าหน้าที่ดำเนินการ
   */
  @Get("swap/{direction}/{operatorId}")
  async swapCommandOperator(@Path() direction: string, @Path() operatorId: string) {
    const source = await this.commandOperatorRepo.findOne({
      where: { id: operatorId },
    });

    if (!source) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลเจ้าหน้าที่");
    }

    const sourceOrder = source.orderNo;
    const isUp = direction.trim().toUpperCase() === "UP";

    let dest: CommandOperator | null;

    if (isUp) {
      dest = await this.commandOperatorRepo.findOne({
        where: {
          commandId: source.commandId,
          orderNo: LessThan(sourceOrder),
        },
        order: { orderNo: "DESC" },
      });
    } else {
      dest = await this.commandOperatorRepo.findOne({
        where: {
          commandId: source.commandId,
          orderNo: MoreThan(sourceOrder),
        },
        order: { orderNo: "ASC" },
      });
    }

    // ถ้าไม่มีตัวให้สลับ (บนสุด / ล่างสุด)
    if (!dest) {
      return new HttpSuccess();
    }

    // swap
    const temp = source.orderNo;
    source.orderNo = dest.orderNo;
    dest.orderNo = temp;

    await Promise.all([this.commandOperatorRepo.save(source), this.commandOperatorRepo.save(dest)]);

    return new HttpSuccess();
  }

  /**
   * API เพิ่มเจ้าหน้าที่ดำเนินการที่คำสั่ง
   * @summary API เพิ่มเจ้าหน้าที่ดำเนินการที่คำสั่ง
   * @param commandId คีย์คำสั่ง
   */
  @Post("{commandId}")
  async createCommandOperators(
    @Path() commandId: string,
    @Request() request: RequestWithUser,
    @Body() body: CreateCommandOperatorDto,
  ) {
    const command = await this.commandRepo.findOne({
      where: { id: commandId },
      select: { id: true },
    });

    if (!command) {
      throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้");
    }
    const lastOrderNo = await this.commandOperatorRepo.findOne({
      where: { commandId: commandId },
      order: { orderNo: "DESC" },
      select: { orderNo: true },
    });
    const nextOrderNo = (lastOrderNo?.orderNo ?? 1) + 1;

    const now = new Date();
    const operator = Object.assign(new CommandOperator(), {
      ...body,
      commandId: command.id,
      orderNo: nextOrderNo,
      createdUserId: request.user.sub,
      createdFullName: request.user.name,
      createdAt: now,
      lastUpdateUserId: request.user.sub,
      lastUpdateFullName: request.user.name,
      lastUpdatedAt: now,
    });
    await this.commandOperatorRepo.save(operator);
    return new HttpSuccess();
  }

  /**
   * API ลบเจ้าหน้าที่ดำเนินการที่คำสั่ง
   * @summary API ลบเจ้าหน้าที่ดำเนินการที่คำสั่ง
   * @param commandId คีย์คำสั่ง
   * @param operatorId คีย์เจ้าหน้าที่ดำเนินการ
   */
  @Delete("{commandId}/{operatorId}")
  public async deleteCommandOperator(@Path() commandId: string, @Path() operatorId: string) {
    const queryRunner = AppDataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // 1. หา operator
      const operator = await queryRunner.manager.findOne(CommandOperator, {
        where: {
          id: operatorId,
          commandId: commandId,
        },
      });

      if (!operator) {
        throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบเจ้าหน้าที่ดำเนินการ");
      }

      // // 2. ห้ามลบ orderNo = 1
      // if (operator.orderNo === 1) {
      //   throw new HttpError(
      //     HttpStatusCode.BAD_REQUEST,
      //     "ไม่สามารถลบเจ้าหน้าที่ลำดับที่ 1 ได้"
      //   );
      // }

      const removedOrderNo = operator.orderNo;

      // 3. ลบ
      await queryRunner.manager.remove(operator);

      // 4. re orderNumber ตัวที่เหลือ
      await queryRunner.manager
        .createQueryBuilder()
        .update(CommandOperator)
        .set({
          orderNo: () => "orderNo - 1",
        })
        .where("commandId = :commandId", { commandId })
        .andWhere("orderNo > :removedOrderNo", { removedOrderNo })
        .execute();

      await queryRunner.commitTransaction();
      return new HttpSuccess(true);
    } catch (error) {
      await queryRunner.rollbackTransaction();
      console.error("Delete command operator error:", error);
    } finally {
      await queryRunner.release();
    }
  }
}
