import { OrgRevision } from "../entities/OrgRevision";
import { AppDataSource } from "../database/data-source";
import { PosMaster } from "../entities/PosMaster";
import { Position } from "../entities/Position";
import { EmployeePosMaster } from "../entities/EmployeePosMaster";
import { EmployeePosition } from "../entities/EmployeePosition";
import { In, IsNull, MoreThan, Not } from "typeorm";
import { RequestWithUser } from "../middlewares/user";
import { Command } from "../entities/Command";
import { ProfileSalary } from "../entities/ProfileSalary";
import { Profile } from "../entities/Profile";
import { ProfileEmployee } from "../entities/ProfileEmployee";
import { CommandRecive } from "../entities/CommandRecive";
import { PosMasterAct } from "../entities/PosMasterAct";
export function calculateAge(start: Date, end = new Date()) {
  if (start.getTime() > end.getTime()) return null;

  let year = end.getFullYear() - start.getFullYear();
  let month = end.getMonth() - start.getMonth();
  let day = end.getDate() - start.getDate();

  // if (month < 0) {
  //   month += 12;
  //   year -= 1;
  // }
  // if (day < 0) {
  //   month -= 1;
  //   day =
  //     new Date(start.getFullYear(), start.getMonth(), 0).getDate() -
  //     start.getDate() +
  //     end.getDate();
  // }

  if (day < 0) {
    month -= 1;
    day += new Date(end.getFullYear(), end.getMonth(), 0).getDate();
  }

  if (month < 0) {
    month += 12;
    year -= 1;
  }

  return { year, month, day };
}

export async function calculateGovAge(profileId: string, type: string, bkk?: boolean) {
  // type = OFFICER , EMPLOYEE
  const isEmployee = type === "EMPLOYEE";

  // const records = await AppDataSource.getRepository(ProfileSalary).find({
  //   where: {
  //     [isEmployee ? "profileEmployeeId" : "profileId"]: profileId,
  //   },
  //   select: ["commandDateAffect", "dateGovernment", "isGovernment"],
  //   order: { order: "ASC" },
  // });

  // if (!records || records.length === 0) {
  //   return null;
  // }

  let endDateFristRec: any = null;
  endDateFristRec = await AppDataSource.getRepository(ProfileSalary).findOne({
    where: {
      [isEmployee ? "profileEmployeeId" : "profileId"]: profileId,
      dateGovernment: Not(IsNull()),
      isGovernment: false,
    },
    select: ["dateGovernment", "order"],
    order: { order: "ASC" },
  });
  // console.log("endDateFristRec", endDateFristRec.dateGovernment);

  const calculateDuration = (startDate: any, endDate: any, inclusive = false) => {
    if (inclusive) {
      endDate = new Date(endDate.getTime() + 86400000); // +1 วัน
    }
    let years = endDate.getFullYear() - startDate.getFullYear();
    let months = endDate.getMonth() - startDate.getMonth();
    let days = endDate.getDate() - startDate.getDate();

    if (days < 0) {
      months--;
      const lastMonthDays = new Date(endDate.getFullYear(), endDate.getMonth(), 0).getDate();
      days += lastMonthDays;
    }

    if (months < 0) {
      months += 12;
      years--;
    }

    return { years, months, days };
  };
  let profile: any;
  profile = await AppDataSource.getRepository(Profile).findOne({
    where: {
      id: profileId,
    },
  });
  if (isEmployee) {
    profile = await AppDataSource.getRepository(ProfileEmployee).findOne({
      where: {
        id: profileId,
      },
    });
  }
  // const firstStartDate = new Date(records[0].date);
  const firstStartDate = !bkk
    ? profile?.dateAppoint ? profile?.dateAppoint : new Date()
    : profile?.dateStart ? profile?.dateStart : new Date();
  const firstEndDate = endDateFristRec ? new Date(endDateFristRec.dateGovernment) : new Date();

  // console.log("firstStartDate1", firstStartDate);
  // console.log("firstEndDate1", firstEndDate);

  const {
    years: totalYears1,
    months: totalMonths1,
    days: totalDays1,
  } = calculateDuration(firstStartDate, firstEndDate);

  const records_middle = await AppDataSource.getRepository(ProfileSalary).find({
    where: {
      [isEmployee ? "profileEmployeeId" : "profileId"]: profileId,
      order: MoreThan(endDateFristRec?.order),
      dateGovernment: Not(IsNull()),
    },
    select: ["dateGovernment", "isGovernment"],
    order: { order: "ASC" },
  });
  //ถ้าไม่มีเข้าออกราชการหลายครั้ง
  if (!records_middle || records_middle.length === 0) {
    return { year: totalYears1, month: totalMonths1, day: totalDays1 };
  }

  let totalYears2 = 0,
    totalMonths2 = 0,
    totalDays2 = 0;

  // case ที่เข้าออกราชการหลายครั้ง
  for (let i = 0; i < records_middle.length; i++) {
    const current = records_middle[i];
    const next = records_middle[i + 1];

    // นับเฉพาะช่วงที่เป็นราชการ
    if (current.isGovernment === true) {
      const startDate = new Date(current.dateGovernment);
      const endDate = next ? new Date(next.dateGovernment) : new Date();
      const { years, months, days } = calculateDuration(startDate, endDate);

      totalYears2 += years;
      totalMonths2 += months;
      totalDays2 += days;

      // console.log(`✔ นับช่วง ${startDate.toISOString()} → ${endDate.toISOString()} ได้ ${years} ปี ${months} เดือน ${days} วัน`);
    }
    // else {
    //   console.log(`❌ ไม่รวมช่วง ${current.dateGovernment} เพราะ isGovernment = false`);
    // }
  }

  //ตั้งแต่วันที่กลับมารับราขการไปจนถึงรวมถึงวันที่ปัจจุบัน
  const adjustTotal = (years: number, months: number, days: number) => {
    const daysInThisMonth = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0).getDate();
    if (days >= daysInThisMonth) {
      months += Math.floor(days / daysInThisMonth);
      days %= daysInThisMonth;
    }

    if (months >= 12) {
      years += Math.floor(months / 12);
      months %= 12;
    }

    return { years, months, days };
  };

  let sumYears = totalYears1 + totalYears2;
  let sumMonths = totalMonths1 + totalMonths2;
  let sumDays = totalDays1 + totalDays2;
  // console.log("totalYears1", totalYears1);
  // console.log("totalMonths1", totalMonths1);
  // console.log("totalDays1", totalDays1);
  // console.log("totalYears2", totalYears2);
  // console.log("totalMonths2", totalMonths2);
  // console.log("totalDays2", totalDays2);
  // console.log("sumYears", sumYears);
  // console.log("sumMonths", sumMonths);
  // console.log("sumDays", sumDays);
  const finalAdjusted = adjustTotal(sumYears, sumMonths, sumDays);

  return {
    year: finalAdjusted.years,
    month: finalAdjusted.months,
    day: finalAdjusted.days,
  };
}

export function calculateRetireDate(birthDate: Date) {
  //   let _birthDate = birthDate;

  //   _birthDate.setFullYear(_birthDate.getFullYear() + 60); var year = d.getFullYear();
  var d = birthDate;
  var year = d.getFullYear();
  var month = d.getMonth();
  var day = d.getDate();
  var c = new Date(year + 60, month, day - 1);
  return c;
}
export function calculateRetireLaw(birthDate: Date) {
  let dd = birthDate.getDate();
  let mm = birthDate.getMonth();
  let yy = birthDate.getFullYear();

  let flag = true;

  switch (mm) {
    case 9:
      if (dd >= 2) flag = false;
      break;
    case 10:
      flag = false;
      break;
    case 11:
      flag = false;
      break;
  }

  if (flag == true) return new Date(`${yy + 60}-09-30T00:00:00.000+07:00`);

  return new Date(`${yy + 61}-09-30T00:00:00.000+07:00`);
}

export function calculateRetireYear(birthDate: Date) {
  let dd = birthDate.getDate();
  let mm = birthDate.getMonth();
  let yy = birthDate.getFullYear();

  let flag = true;

  switch (mm) {
    case 10:
      if (dd >= 2) flag = false;
      break;
    case 11:
      break;
    case 12:
      break;
  }

  if (flag) return yy + 60;

  return yy + 61;
}
export async function removeProfileInOrganize(profileId: string, type: string) {
  const currentRevision = await AppDataSource.getRepository(OrgRevision)
    .createQueryBuilder("orgRevision")
    .where("orgRevision.orgRevisionIsDraft = false")
    .andWhere("orgRevision.orgRevisionIsCurrent = true")
    .getOne();

  const draftRevision = await AppDataSource.getRepository(OrgRevision)
    .createQueryBuilder("orgRevision")
    .where("orgRevision.orgRevisionIsDraft = true")
    .andWhere("orgRevision.orgRevisionIsCurrent = false")
    .getOne();

  if (!currentRevision && !draftRevision) {
    return;
  }
  if (type === "OFFICER") {
    const findProfileInposMaster = await AppDataSource.getRepository(PosMaster)
      .createQueryBuilder("posMaster")
      .where("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: currentRevision?.id })
      .andWhere("posMaster.current_holderId = :profileId", { profileId })
      .getOne();

    await AppDataSource.getRepository(PosMaster)
      .createQueryBuilder()
      .update(PosMaster)
      .set({ current_holderId: null, isSit: false })
      .where("id = :id", { id: findProfileInposMaster?.id })
      .execute();

    const findProfileInposMasterDraft = await AppDataSource.getRepository(PosMaster)
      .createQueryBuilder("posMaster")
      .where("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: draftRevision?.id })
      .andWhere("posMaster.next_holderId = :profileId", { profileId })
      .getOne();

    await AppDataSource.getRepository(PosMaster)
      .createQueryBuilder()
      .update(PosMaster)
      .set({ next_holderId: null, isSit: false })
      .where("id = :id", { id: findProfileInposMasterDraft?.id })
      .execute();

    if (!findProfileInposMaster && !findProfileInposMasterDraft) {
      return;
    }
    const findPosition = await AppDataSource.getRepository(Position)
      .createQueryBuilder("position")
      .where("position.posMasterId = :posMasterId", { posMasterId: findProfileInposMaster?.id })
      .getMany();

    if (!findPosition) {
      return;
    }
    await AppDataSource.getRepository(Position)
      .createQueryBuilder()
      .update(Position)
      .set({ positionIsSelected: false })
      .where("id IN (:...ids)", { ids: findPosition.map((item) => item.id) })
      .execute();
  }
  if (type === "EMPLOYEE") {
    const findProfileInEmpPosMaster = await AppDataSource.getRepository(EmployeePosMaster)
      .createQueryBuilder("employeePosMaster")
      .where("employeePosMaster.orgRevisionId = :orgRevisionId", {
        orgRevisionId: currentRevision?.id,
      })
      .andWhere("employeePosMaster.current_holderId = :profileId", { profileId })
      .getOne();
    await AppDataSource.getRepository(EmployeePosMaster)
      .createQueryBuilder()
      .update(EmployeePosMaster)
      .set({ current_holderId: null, isSit: false })
      .where("id = :id", { id: findProfileInEmpPosMaster?.id })
      .execute();

    if (!findProfileInEmpPosMaster) {
      return;
    }
    const findEmpPosition = await AppDataSource.getRepository(EmployeePosition)
      .createQueryBuilder("employeePosition")
      .where("employeePosition.posMasterId = :posMasterId", {
        posMasterId: findProfileInEmpPosMaster?.id,
      })
      .getMany();

    if (!findEmpPosition) {
      return;
    }

    await AppDataSource.getRepository(EmployeePosition)
      .createQueryBuilder()
      .update(EmployeePosition)
      .set({ positionIsSelected: false })
      .where("id IN (:...ids)", { ids: findEmpPosition.map((item) => item.id) })
      .execute();
  }
}

export async function removePostMasterAct(profileId: string) {
  const currentRevision = await AppDataSource.getRepository(OrgRevision)
    .createQueryBuilder("orgRevision")
    .where("orgRevision.orgRevisionIsDraft = false")
    .andWhere("orgRevision.orgRevisionIsCurrent = true")
    .getOne();

  if (!currentRevision) {
    return;
  }

  const findProfileInposMaster = await AppDataSource.getRepository(PosMaster)
    .createQueryBuilder("posMaster")
    .where("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: currentRevision?.id })
    .andWhere("posMaster.current_holderId = :profileId", { profileId })
    .getOne();

  if (!findProfileInposMaster) {
    return;
  }

  const posMasterAct = await AppDataSource.getRepository(PosMasterAct)
    .createQueryBuilder("posMasterAct")
    .where("posMasterAct.posMasterChildId = :posMasterChildId", { posMasterChildId: findProfileInposMaster.id })
    .getMany();
  await AppDataSource.getRepository(PosMasterAct).remove(posMasterAct);
}

export async function checkReturnCommandType(commandId: string) {
  const commandRepository = AppDataSource.getRepository(Command);
  const _type = await commandRepository.findOne({
    where: {
      id: commandId,
    },
    relations: ["commandType"],
  });
  if (!["C-PM-08", "C-PM-09"].includes(String(_type?.commandType.code))) {
    return false;
  }
  return true;
}

export async function checkCommandType(commandId: string) {
  const commandRepository = AppDataSource.getRepository(Command);
  const commandReciveRepository = AppDataSource.getRepository(CommandRecive);
  const _type = await commandRepository.findOne({
    where: {
      id: commandId,
    },
    relations: ["commandType", "commandRecives"],
  });

  if (
    ![
      "C-PM-12",
      "C-PM-13",
      "C-PM-17",
      "C-PM-18",
      "C-PM-23",
      "C-PM-19",
      "C-PM-20",
      "C-PM-25",
      "C-PM-26",
      "C-PM-43",
      "C-PM-48"
    ].includes(String(_type?.commandType.code))
  ) {
    // return false;
    return { status: false, LeaveType: null, leaveRemark: null, retireTypeName: null };
  }
  // return true;
  const _commandRecive = await commandReciveRepository.findOne({
    where: { commandId: commandId },
  });

  let _leaveType: string = "";
  let _retireTypeName: string = ""; //อิงตามเหตุผลการพ้นจากราชการ ข้อมูลทะเบียนประวัติผู้พ้นจากราชการ
  switch (String(_type?.commandType.code)) {
    case "C-PM-12": {
      _leaveType = "PROBATION_REPORT";
      _retireTypeName = "ผลการทดลองฯ ต่ำกว่ามาตรฐานที่กำหนด"
      break;
    }
    case "C-PM-13": {
      _leaveType = "PLACEMENT_TRANSFER";
      _retireTypeName = "โอนออก";
      break;
    }
    case "C-PM-17": {
      _leaveType = "RETIRE_RESIGN";
      _retireTypeName = "ลาออกจากราชการ";
      break;
    }
    case "C-PM-18": {
      _leaveType = "RETIRE_OUT";
      _retireTypeName = "ให้ออกจากราชการ";
      break;
    }
    case "C-PM-19": {
      _leaveType = "DISCIPLINE_RESULT_REMOVE";
      _retireTypeName = "ปลดออกจากราชการ";
      break;
    }
    case "C-PM-20": {
      _leaveType = "DISCIPLINE_RESULT_DISMISS";
      _retireTypeName = "ไล่ออกจากราชการ";
      break;
    }
    case "C-PM-23": {
      _leaveType = "RETIRE_RESIGN_EMP";
      _retireTypeName = "ลาออกจากราชการ";
      break;
    }
    case "C-PM-25": {
      _leaveType = "DISCIPLINE_SUSPEND";
      _retireTypeName = "พักจากราชการ";
      break;
    }
    case "C-PM-26": {
      _leaveType = "DISCIPLINE_TEMP_SUSPEND";
      _retireTypeName = "ให้ออกจากราชการไว้ก่อน";
      break;
    }
    case "C-PM-43": {
      _leaveType = "RETIRE_OUT_EMP";
      _retireTypeName = "ให้ออกจากราชการ";
      break;
    }
    case "C-PM-48": {
      _leaveType = "RETIRE_MILITARY";
      _retireTypeName = "รับราชการทหาร";
      break;
    }
    default: {
      _leaveType = "";
      _retireTypeName = "";
    }
  }
  return {
    status: true,
    LeaveType: _leaveType,
    leaveRemark: _commandRecive ? _commandRecive.remarkVertical : null,
    retireTypeName: _retireTypeName ? _retireTypeName : null
  };
}

//logs
export type DataDiff = {
  before: any;
  after: any;
};

export type LogSequence = {
  action: string;
  status: "success" | "error";
  description: string;
  query?: any;
  request?: {
    method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
    url?: string;
    payload?: string;
    response?: string;
  };
};

export function setLogDataDiff(req: RequestWithUser, data: DataDiff) {
  // Check if data.before and data.after are valid objects
  if (
    data.before &&
    typeof data.before === "object" &&
    data.after &&
    typeof data.after === "object"
  ) {
    req.app.locals.logData.dataDiff = {
      before: JSON.stringify(data.before),
      after: JSON.stringify(data.after),
    };
  } else {
    console.error("Invalid data provided: both before and after must be valid objects.");
  }
}

export function addLogSequence(req: RequestWithUser, data: LogSequence) {
  if (!req?.app?.locals?.logData?.sequence) {
    req.app.locals.logData.sequence = [];
  }
  req.app.locals.logData.sequence = req.app.locals.logData.sequence.concat(data);
}

export function editLogSequence(req: RequestWithUser, index: number, data: LogSequence) {
  req.app.locals.logData.sequence[index] = data;
}

export async function checkQueueInProgress(queueName: string) {
  const axios = require('axios');
  // console.log("Checking queue in progress");
  const res = await axios.get(`${process.env.RABBIT_API_URL}/api/queues/%2F/${queueName}`, {
    auth: { username: process.env.RABBIT_USER, password: process.env.RABBIT_PASS },
  });

  const q = res.data;

  // console.log(`Queue "${queueName}" has:`);
  // console.log(` - ${q.messages_ready} messages ready`);
  // console.log(` - ${q.messages_unacknowledged} messages in progress (unacked)`);

  if (q.messages_unacknowledged > 0) {
    return true;
  }

  return false;
}

export function chunkArray(array: any, size: number) {
  const result = [];
  for (let i = 0; i < array.length; i += size) {
    result.push(array.slice(i, i + size));
  }
  return result;
}

export async function PayloadSendNoti(commandId: string) {
  if (!commandId)
    return "";
  const commandRepository = AppDataSource.getRepository(Command);
  const _command = await commandRepository.findOne({
    where: {
      id: commandId,
    },
    relations: ["commandType", "commandRecives"],
  });
  if (!_command || !_command.commandType)
    return "";
  const _payload = {
    name: _command && _command.commandType
      ? `คำสั่ง${_command.commandType.name}`
      : "",
    url: `${process.env.API_URL}/salary/file/ระบบออกคำสั่ง/คำสั่ง/${commandId}/คำสั่ง`,
    isReport: true,
    isTemplate: false,
  }
  let attachments = {}
  if (_command.commandType.isUploadAttachment === true) {

    const attachmentPayloads = _command.commandRecives.map((recive: any) => ({
      name: `เอกสารแนบท้ายคำสั่ง${_command.commandType.name} (${recive.prefix}${recive.firstName} ${recive.lastName})`,
      url: `${process.env.API_URL}/salary/sub-file/ระบบออกคำสั่ง/แนบท้าย/${commandId}/${recive.citizenId}/แนบท้าย`,
      isReport: true,
      isTemplate: false,
    }));

    attachments = {
      attachments: [_payload, ...attachmentPayloads]
    };
  } else {
    attachments = {
      attachments: [_payload]
    };
  }
  return JSON.stringify(attachments);
}

export function commandTypePath(commandCode: string): string | null {
  switch (commandCode) {
    case "C-PM-01":
      return "/placement/recruit/report"; //
    case "C-PM-02":
      return "/placement/candidate/report"; //
    case "C-PM-03":
      return "/placement/appoint/report";
    case "C-PM-04":
      return "/placement/move/report";
    case "C-PM-05":
      return "/placement/appointment/appoint/report";
    case "C-PM-06":
      return "/placement/slip/report";
    case "C-PM-07":
      return "/placement/appointment/move/report";
    case "C-PM-08":
      return "/retirement/other/appoint/report";
    case "C-PM-09":
      return "/retirement/other/out/report";
    case "C-PM-10":
      return "/probation/report/command10/officer/report";
    case "C-PM-11":
      return "/probation/report/command11/officer/report"; //PROBATION
    case "C-PM-12":
      return "/probation/report/command12/officer/report"; //PROBATION
    case "C-PM-13":
      return "/placement/transfer/command/report";
    case "C-PM-14":
      return "/placement/receive/command/report";
    case "C-PM-15":
      return "/placement/officer/command/report";
    case "C-PM-16":
      return "/placement/repatriation/command/report";
    case "C-PM-17":
      return "/retirement/resign/command/report";
    case "C-PM-18":
      return "/retirement/out/command/report";
    case "C-PM-19":
      return "/discipline/result/command19/report";
    case "C-PM-20":
      return "/discipline/result/command20/report";
    case "C-PM-21":
      return "/org/command/command21/employee/report"; //ORG
    case "C-PM-22":
      return "/placement/appointment/employee-appoint/report";
    case "C-PM-23":
      return "/retirement/resign-employee/command/report";
    case "C-PM-24":
      return "/placement/appointment/employee-move/report";
    case "C-PM-25":
      return "/discipline/result/command25/report";
    case "C-PM-26":
      return "/discipline/result/command26/report";
    case "C-PM-27":
      return "/discipline/result/command27/report";
    case "C-PM-28":
      return "/discipline/result/command28/report";
    case "C-PM-29":
      return "/discipline/result/command29/report";
    case "C-PM-30":
      return "/discipline/result/command30/report";
    case "C-PM-31":
      return "/discipline/result/command31/report";
    case "C-PM-32":
      return "/discipline/result/command32/report";
    case "C-PM-33":
      return "/salary/report/command33/officer/report"; //SALARY
    case "C-PM-34":
      return "/salary/report/command34/officer/report"; //SALARY
    case "C-PM-35":
      return "/salary/report/command35/officer/report"; //SALARY
    case "C-PM-36":
      return "/salary/report/command36/employee/report"; //SALARY
    case "C-PM-37":
      return "/salary/report/command37/employee/report"; //SALARY
    case "C-PM-38":
      return "/org/command/command38/officer/report"; //ORG
    case "C-PM-39":
      return "/placement/appointment/slip/report";
    case "C-PM-40":
      return "/org/command/command40/officer/report"; //ORG
    case "C-PM-41":
      return "/retirement/resign/leave-cancel/report";
    case "C-PM-42":
      return "/retirement/resign-employee/leave-cancel/report";
    case "C-PM-43":
      return "/retirement/out/command/report";
    case "C-PM-45":
      return "/salary/report/command35/officer/report"; //SALARY
    case "C-PM-46":
      return "/salary/report/command36/employee/report"; //SALARY
    case "C-PM-47":
      return "/placement/appointment/gazette/report";
    case "C-PM-48":
      return "/retirement/resign/command48/report";
    default:
      return null;
  }
}

export function resolveNodeLevel(data: any) {
  if (data.child4DnaId) return 4;
  if (data.child3DnaId) return 3;
  if (data.child2DnaId) return 2;
  if (data.child1DnaId) return 1;
  if (data.rootDnaId) return 0;
  return null;
}

export function resolveNodeId(data: any) {
  return (
    data.child4DnaId ??
    data.child3DnaId ??
    data.child2DnaId ??
    data.child1DnaId ??
    data.rootDnaId ??
    null
  );
}

export function logPositionIsSelectedChange(
  positionId: string,
  oldValue: boolean,
  newValue: boolean,
  context: {
    posMasterId?: string;
    userId?: string;
    endpoint?: string;
    action?: string;
  }
) {
  if (oldValue !== newValue) {
    console.log(`[positionIsSelected-DEBUG] Position ${positionId}: ${oldValue} -> ${newValue}`, {
      ...context,
      timestamp: new Date().toISOString(),
    });
  }
}