import { Injectable } from "@nestjs/common"
import { CreateKpiDto } from "./dto/create-kpi.dto"
import { verifyJwtToken } from "src/utils/jwt"
import { failureResponse, successResponse } from "src/common/response/response"
import {
  canFillMIS,
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { messageKey } from "src/constants/message-keys"
import { code } from "src/common/response/response.code"
import { MisBusinessKpiRepository } from "./repositories/business-kpi.repository"
import { MisBusinessKpis } from "./entities/mis-business-kpis.entity"
import { KpiListFiltersDto } from "./dto/kpi-list-filters.dto"
import { MisReportRepository } from "./repositories/mis-report.repository"
import { MisReportRatingFeedbackRepository } from "./repositories/mis-report-rating-feedback.repository"
import { BusinessKpiRatingRepository } from "./repositories/business-kpi-rating.repository"
import { MyMisFiltersDto } from "./dto/my-mis-filters.dto"
import { MyMisDto } from "./dto/my-mis.dto"
import { MisActionFiltersDto } from "./dto/mis-action-filters.dto"
import { MisActionDto } from "./dto/mis-action.dto"
import { MisReport } from "./entities/mis-report.entity"
import { MisReportRatingFeedback } from "./entities/mis-report-rating-feedback.entity"
import { BusinessKpiRating } from "./entities/business-kpi-rating.entity"
import { Repository } from "typeorm"
import { InjectRepository } from "@nestjs/typeorm"
import moment from "moment"
import { CompanySettingsRepository } from "../company/repositories/company-settings.repository"

@Injectable()
export class MisService {
  constructor(
    private readonly misBusinessKpisRepository: MisBusinessKpiRepository,
    private readonly misReportRepository: MisReportRepository,
    private readonly misReportRatingFeedbackRepository: MisReportRatingFeedbackRepository,
    private readonly companySettingRepository: CompanySettingsRepository,
    private readonly businessKpiRatingRepository: BusinessKpiRatingRepository,
    @InjectRepository(MisReport)
    private readonly misReportEntityRepository: Repository<MisReport>,
    @InjectRepository(BusinessKpiRating)
    private readonly businessKpiRatingEntityRepository: Repository<BusinessKpiRating>,
  ) {}

  async createKpi(body: CreateKpiDto, token: string) {
    const decoded = verifyJwtToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    try {
      const { name, department_ids } = body

      const companyId = decoded?.company_id

      const alreadyKpiExist = await this.misBusinessKpisRepository.getByParams({
        where: {
          name: name.trim(),
          company_id: companyId,
        },
        findOne: true,
      })

      if (alreadyKpiExist) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "KPI",
          }),
        )
      }

      const kpi = new MisBusinessKpis()

      kpi.company_id = companyId
      kpi.name = name.trim()
      kpi.departments = department_ids.map((id) => ({ id }) as any)

      await this.misBusinessKpisRepository.save(kpi)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_add, { ":data": "KPI" }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async fetchAllKpis(filters: KpiListFiltersDto, token: string) {
    try {
      const decoded = verifyJwtToken(token)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      const { page = 1, limit = 10, search, department_id } = filters

      const skip = (page - 1) * limit
      const take = parseInt(limit.toString())
      const whereConditions: any = {}
      const searchConditions: any = {}

      if (decoded.company_id) {
        whereConditions.company_id = decoded.company_id
      }

      if (department_id) {
        whereConditions["departments.id"] = department_id
      }

      if (search) {
        searchConditions.name = search
      }

      const [rows, count]: any =
        await this.misBusinessKpisRepository.fetchKpisWithFilters({
          companyId: decoded.company_id,
          departmentId: department_id,
          search,
          skip,
          take,
        })

      const data: any = {
        count,
        data: rows,
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, { ":data": "KPI" }),
        data,
      )
    } catch (error) {
      console.log(error)

      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async fetchByKpiId(id: number, token: string) {
    const decoded = verifyJwtToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    try {
      const data: any = await this.misBusinessKpisRepository.getByParams({
        where: {
          id: id,
          company_id: decoded.company_id,
        },
        relations: ["departments:id,name"],
        select: ["id", "name"],
        findOne: true,
      })

      if (isEmpty(data)) {
        return failureResponse(
          code.ERROR,
          validationMessage(messageKey.data_not_found),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, { ":data": "KPI" }),
        data,
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async updateKpi(id: number, body: CreateKpiDto, token: string) {
    const decoded = verifyJwtToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    try {
      const { name, department_ids } = body
      const companyId = decoded.company_id
      const trimmedName = name.trim()

      const existingKpi: any = await this.misBusinessKpisRepository.getByParams(
        {
          where: { id, company_id: companyId },
          findOne: true,
        },
      )

      if (!existingKpi) {
        return failureResponse(
          code.ERROR,
          validationMessage(messageKey.not_found, {
            ":data": "KPI",
          }),
        )
      }

      const isTaken = await this.misBusinessKpisRepository.isKpiNameTaken(
        trimmedName,
        companyId,
        id,
      )

      if (isTaken) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "KPI",
          }),
        )
      }

      existingKpi.name = trimmedName
      existingKpi.departments = department_ids.map(
        (depId) => ({ id: depId }) as any,
      )

      await this.misBusinessKpisRepository.save(existingKpi)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, { ":data": "KPI" }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  // My MIS APIs
  async getMyMis(filters: MyMisFiltersDto, token: string) {
    try {
      const decoded = verifyJwtToken(token)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      const { page = 1, limit = 10, year } = filters
      const skip = (page - 1) * limit
      const take = parseInt(limit.toString())

      // Get current date info
      const currentDate = new Date()
      const currentMonth = currentDate.getMonth() + 1
      const currentYear = currentDate.getFullYear()

      // Get all existing MIS reports for the employee
      const queryBuilder = this.misReportEntityRepository
        .createQueryBuilder("mis_reports")
        .leftJoin(
          "mis_report_rating_feedbacks",
          "feedback",
          "feedback.employee_id = mis_reports.employee_id AND feedback.month = mis_reports.month AND feedback.year = mis_reports.year",
        )
        .where("mis_reports.employee_id = :employeeId", {
          employeeId: decoded.employee_id,
        })
        .andWhere("mis_reports.company_id = :companyId", {
          companyId: decoded.company_id,
        })

      if (year) {
        queryBuilder.andWhere("mis_reports.year = :year", { year })
      }

      const existingReports = await queryBuilder
        .select([
          "mis_reports.id as id",
          "mis_reports.month as month",
          "mis_reports.year as year",
          "mis_reports.status as status",
          "feedback.business_rating as business_rating",
          "feedback.technical_rating as technical_rating",
          "feedback.personal_rating as personal_rating",
          "feedback.average_rating as average_rating",
        ])
        .orderBy("mis_reports.year", "DESC")
        .addOrderBy("mis_reports.month", "DESC")
        .getRawMany()

      // Convert existing reports
      const allMonthsData = existingReports.map((report: any) => ({
        id: report.id,
        month: moment()
          .month(report.month - 1)
          .format("MMMM"),
        year: report.year,
        status: report.status,
        business_rating: Number(report.business_rating) || 0,
        technical_rating: Number(report.technical_rating) || 0,
        personal_rating: Number(report.personal_rating) || 0,
        average_rating: Number(report.average_rating) || 0,
      }))

      // =========================
      // CURRENT MONTH LOGIC
      // =========================
      const currentMonthExists = existingReports.some(
        (report) =>
          report.month === currentMonth && report.year === currentYear,
      )

      const shouldAddCurrentMonth =
        (!year || year === currentYear) && !currentMonthExists

      if (shouldAddCurrentMonth) {
        const currentMonthRecord = {
          id: null,
          month: moment()
            .month(currentMonth - 1)
            .format("MMMM"),
          year: currentYear,
          status: null,
          business_rating: 0,
          technical_rating: 0,
          personal_rating: 0,
          average_rating: 0,
        }

        allMonthsData.unshift(currentMonthRecord)
      }

      // =========================
      // PAGINATION
      // =========================
      const totalCount = allMonthsData.length
      const paginatedData = allMonthsData.slice(skip, skip + take)

      const data: any = {
        count: totalCount,
        data: paginatedData,
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, { ":data": "MIS Reports" }),
        data,
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async createOrUpdateMyMis(body: MyMisDto, token: string) {
    try {
      const decoded = verifyJwtToken(token)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      const {
        id,
        month,
        year,
        business_tasks,
        technical_tasks,
        personal_tasks,
        status,
      } = body

      const companySettings: any =
        await this.companySettingRepository.getByParams({
          where: { company_id: decoded.company_id },
          findOne: true,
        })

      if (companySettings?.last_day_to_fill_mis_report) {
        if (
          !canFillMIS(companySettings?.last_day_to_fill_mis_report, month, year)
        ) {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.mis_submission_not_allowed),
          )
        }
      }

      let misReport: MisReport

      if (id) {
        // Update case
        misReport = (await this.misReportRepository.getByParams({
          where: {
            id,
            employee_id: decoded.employee_id,
            company_id: decoded.company_id,
          },
          findOne: true,
        })) as any

        if (!misReport) {
          return failureResponse(
            code.ERROR,
            validationMessage(messageKey.not_found, { ":data": "MIS Report" }),
          )
        }

        misReport.month = month
        misReport.year = year
        misReport.business_tasks = business_tasks
        misReport.technical_tasks = technical_tasks
        misReport.personal_tasks = personal_tasks
        misReport.status = status
      } else {
        // Create case
        const existingReport = await this.misReportRepository.getByParams({
          where: {
            employee_id: decoded.employee_id,
            company_id: decoded.company_id,
            month,
            year,
          },
          findOne: true,
        })

        if (existingReport) {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.already_exist, {
              ":data": "MIS Report for this month and year",
            }),
          )
        }

        misReport = new MisReport()

        misReport.company_id = decoded.company_id
        misReport.employee_id = decoded.employee_id
        misReport.month = month
        misReport.year = year
        misReport.business_tasks = business_tasks
        misReport.technical_tasks = technical_tasks
        misReport.personal_tasks = personal_tasks
        misReport.status = status
      }

      const savedMisReport = await this.misReportRepository.save(misReport)

      const responseData: any = {
        id: savedMisReport.id,
        month: savedMisReport.month,
        year: savedMisReport.year,
        business_tasks: savedMisReport.business_tasks,
        technical_tasks: savedMisReport.technical_tasks,
        personal_tasks: savedMisReport.personal_tasks,
        status: savedMisReport.status,
        employee_id: savedMisReport.employee_id,
        company_id: savedMisReport.company_id,
        created_at: savedMisReport.created_at,
        updated_at: savedMisReport.updated_at,
      }

      return successResponse(
        code.SUCCESS,
        successMessage(id ? messageKey.data_update : messageKey.data_add, {
          ":data": "MIS Report",
        }),
        responseData,
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async getMyMisDetails(
    employeeId: number,
    token: string,
    month?: number,
    year?: number,
  ) {
    try {
      const decoded = verifyJwtToken(token)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      const queryBuilder = this.misReportEntityRepository
        .createQueryBuilder("mis_reports")
        .leftJoin(
          "mis_report_rating_feedbacks",
          "feedback",
          "feedback.employee_id = mis_reports.employee_id AND feedback.month = mis_reports.month AND feedback.year = mis_reports.year",
        )
        .where("mis_reports.employee_id = :employeeId", { employeeId })
        .andWhere("mis_reports.company_id = :companyId", {
          companyId: decoded.company_id,
        })

      // Add month filter if provided
      if (month) {
        queryBuilder.andWhere("mis_reports.month = :month", { month })
      }

      // Add year filter if provided
      if (year) {
        queryBuilder.andWhere("mis_reports.year = :year", { year })
      }

      const misReport = await queryBuilder
        .select([
          "mis_reports.business_tasks as business_tasks",
          "mis_reports.technical_tasks as technical_tasks",
          "mis_reports.personal_tasks as personal_tasks",
          "mis_reports.status as status",
          "mis_reports.month as month",
          "mis_reports.year as year",
          "feedback.id as feedback_id",
          "feedback.business_feedback as business_feedback",
          "feedback.technical_feedback as technical_feedback",
          "feedback.personal_feedback as personal_feedback",
          "feedback.business_rating as business_rating",
          "feedback.technical_rating as technical_rating",
          "feedback.personal_rating as personal_rating",
          "feedback.average_rating as average_rating",
        ])
        .getRawOne()

      const companySettings: any =
        await this.companySettingRepository.getByParams({
          where: { company_id: decoded.company_id },
          findOne: true,
        })

      let canFillMis = true

      if (companySettings?.last_day_to_fill_mis_report) {
        canFillMis = canFillMIS(
          companySettings?.last_day_to_fill_mis_report,
          month,
          year,
        )
      }

      if (!misReport) {
        // return failureResponse(
        //   code.ERROR,
        //   errorMessage(messageKey.data_not_found, {
        //     ":data": "MIS Report",
        //   }),
        // )

        const data: any = {
          is_mis_editable: canFillMis,
        }

        return successResponse(
          code.SUCCESS,
          successMessage(messageKey.data_retrieve, { ":data": "MIS Actions" }),
          data,
        )
      }

      misReport.is_mis_editable = canFillMis

      // Check if the requested month/year has passed and get employee flags and feedback
      const currentDate = new Date()
      const currentMonth = currentDate.getMonth() + 1
      const currentYear = currentDate.getFullYear()

      // Use the provided month/year or the report's month/year
      const reportMonth = month || misReport.month
      const reportYear = year || misReport.year

      const hasPassed =
        reportYear < currentYear ||
        (reportYear === currentYear && reportMonth < currentMonth)

      // Add flag and feedback details if the period has passed
      if (hasPassed && reportMonth && reportYear) {
        // Get employee flags (flags are not tied to specific months/years)
        const employeeFlags = await this.misReportEntityRepository.query(
          `
          SELECT ef.*, f.name as flag_name, f.status as flag_status
          FROM employee_flags ef
          LEFT JOIN flags f ON ef.flag_id = f.id
          WHERE ef.employee_id = $1 AND ef.company_id = $2 
          AND ef.status = 'active'
          ORDER BY ef.created_at DESC
          LIMIT 1
        `,
          [employeeId, decoded.company_id],
        )

        if (employeeFlags && employeeFlags.length > 0) {
          misReport.flag = employeeFlags[0]
        } else {
          misReport.flag = null
        }

        // Get feedback details for the specific month and year
        const feedbackDetails = await this.misReportEntityRepository.query(
          `
          SELECT 
            business_feedback,
            technical_feedback, 
            personal_feedback,
            business_rating,
            technical_rating,
            personal_rating,
            average_rating,
            created_at,
            updated_at
          FROM mis_report_rating_feedbacks 
          WHERE employee_id = $1 AND company_id = $2 
          AND month = $3 AND year = $4
        `,
          [employeeId, decoded.company_id, reportMonth, reportYear],
        )

        if (feedbackDetails && feedbackDetails.length > 0) {
          misReport.feedback_details = feedbackDetails[0]
        } else {
          misReport.feedback_details = null
        }
      }

      if (month && year) {
        const reportYear = Number(year)
        const reportMonth = Number(month)

        const startDate = new Date(reportYear, reportMonth - 1, 1)
        const endDate = new Date(reportYear, reportMonth, 1)

        const employeeFlags = await this.misReportEntityRepository.query(
          `SELECT ef.id, ef.reason, ef.status, f.name AS flag_name, ef.resolve_at FROM employee_flags ef LEFT JOIN flags f ON ef.flag_id = f.id
                  WHERE ef.employee_id = $1 AND ef.company_id = $2 AND ef.status = 'pending' AND ef.created_at >= $3 AND ef.created_at < $4`,
          [employeeId, decoded.company_id, startDate, endDate],
        )

        misReport.flag =
          employeeFlags && employeeFlags.length > 0
            ? employeeFlags[employeeFlags.length - 1]
            : null
      }

      // Format month name
      misReport.month = moment()
        .month((reportMonth || misReport.month) - 1)
        .format("MMMM")

      misReport.business_rating = Number(misReport.business_rating) || 0
      misReport.technical_rating = Number(misReport.technical_rating) || 0
      misReport.personal_rating = Number(misReport.personal_rating) || 0
      misReport.average_rating = Number(misReport.average_rating) || 0

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "MIS Report Details",
        }),
        misReport,
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async getMisAction(filters: MisActionFiltersDto, token: string) {
    try {
      const decoded = verifyJwtToken(token)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      const {
        // page = 1, limit = 10,
        search,
        status,
        month,
        year,
      } = filters

      // const skip = (page - 1) * limit
      // const take = Number(limit)

      // MAIN QUERY
      // 1. Base Query: Start from Employee to ensure everyone is listed
      // MAIN QUERY
      const queryBuilder = this.misReportEntityRepository.manager
        .createQueryBuilder("employees", "employee") // This already defines the FROM clause
        .distinctOn(["employee.id"])
        .select([
          "employee.id as employee_id",
          "employee.first_name as employee_first_name",
          "employee.last_name as employee_last_name",
          "mis_reports.id as id",
          "mis_reports.month as month",
          "mis_reports.year as year",
          "mis_reports.status as status",
          "feedback.business_rating as business_rating",
          "feedback.technical_rating as technical_rating",
          "feedback.personal_rating as personal_rating",
          "feedback.average_rating as average_rating",
        ])
        // .from("employees", "employee") <-- REMOVE THIS LINE
        .leftJoin(
          "mis_reports",
          "mis_reports",
          `mis_reports.employee_id = employee.id 
     AND mis_reports.company_id = :companyId 
     ${month ? " AND mis_reports.month = :month" : ""} 
     ${year ? " AND mis_reports.year = :year" : ""}`,
          {
            companyId: decoded.company_id,
            month,
            year,
          },
        )
        .leftJoin(
          "mis_report_rating_feedbacks",
          "feedback",
          // Join feedback based on filter values so it works even if mis_reports is null
          `feedback.employee_id = employee.id 
     ${month ? " AND feedback.month = :month" : ""} 
     ${year ? " AND feedback.year = :year" : ""}`,
          { month, year },
        )
        .where("employee.company_id = :companyId", {
          companyId: decoded.company_id,
        })

      if (search) {
        queryBuilder.andWhere(
          "(employee.first_name ILIKE :search OR employee.last_name ILIKE :search)",
          { search: `%${search}%` },
        )
      }

      if (status) {
        // Use IS NULL check if you want to include employees without reports when filtering by a 'pending' status
        queryBuilder.andWhere("mis_reports.status = :status", { status })
      }

      queryBuilder.orderBy("employee.id", "ASC")

      // const rows = await queryBuilder.skip(skip).take(take).getRawMany()
      const rows = await queryBuilder.getRawMany()

      const data: any = {
        count: 0,
        data: await Promise.all(
          rows.map(async (row: any) => {
            const responseData: any = {
              employee: {
                id: row.employee_id,
                first_name: row.employee_first_name,
                last_name: row.employee_last_name,
              },
              status: row.status || null,
              business_rating: Number(row.business_rating) || 0,
              technical_rating: Number(row.technical_rating) || 0,
              personal_rating: Number(row.personal_rating) || 0,
              average_rating: Number(row.average_rating) || 0,
            }

            if (month && year) {
              const reportYear = Number(year)
              const reportMonth = Number(month)

              const startDate = new Date(reportYear, reportMonth - 1, 1)
              const endDate = new Date(reportYear, reportMonth, 1)

              const employeeFlags = await this.misReportEntityRepository.query(
                `SELECT ef.id, ef.reason, ef.status, f.name AS flag_name FROM employee_flags ef LEFT JOIN flags f ON ef.flag_id = f.id
                  WHERE ef.employee_id = $1 AND ef.company_id = $2 AND ef.status = 'pending' AND ef.created_at >= $3 AND ef.created_at < $4`,
                [row.employee_id, decoded.company_id, startDate, endDate],
              )

              responseData.flag =
                employeeFlags && employeeFlags.length > 0
                  ? employeeFlags[employeeFlags.length - 1]
                  : null
            }

            return responseData
          }),
        ),
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, { ":data": "MIS Actions" }),
        data,
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async createOrUpdateMisAction(body: MisActionDto, token: string) {
    try {
      const decoded = verifyJwtToken(token)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      const {
        id,
        month,
        year,
        business_feedback,
        technical_feedback,
        personal_feedback,
        business_rating,
        technical_rating,
        personal_rating,
        kpi_ratings,
        employee_id,
        report_id,
      } = body

      let ratingFeedback: MisReportRatingFeedback

      // Calculate average rating
      const ratings = [
        business_rating,
        technical_rating,
        personal_rating,
      ].filter((r) => r !== undefined && r !== null)

      const average_rating =
        ratings.length > 0
          ? Math.round(
              (ratings.reduce((sum, rating) => sum + rating, 0) /
                ratings.length) *
                2,
            ) / 2
          : 0

      if (id) {
        // Update case
        ratingFeedback =
          (await this.misReportRatingFeedbackRepository.getByParams({
            where: { id },
            findOne: true,
          })) as any

        if (!ratingFeedback) {
          return failureResponse(
            code.ERROR,
            errorMessage(messageKey.not_found, {
              ":data": "Rating Feedback",
            }),
          )
        }

        ratingFeedback.month = month
        ratingFeedback.year = year
        ratingFeedback.business_feedback = business_feedback
        ratingFeedback.technical_feedback = technical_feedback
        ratingFeedback.personal_feedback = personal_feedback
        ratingFeedback.business_rating = business_rating || 0
        ratingFeedback.technical_rating = technical_rating || 0
        ratingFeedback.personal_rating = personal_rating || 0
        ratingFeedback.average_rating = average_rating

        if (decoded?.user_id) ratingFeedback.updated_by = decoded.user_id
      } else {
        // Create case

        const existingRatingFeedback =
          await this.misReportRatingFeedbackRepository.getByParams({
            where: {
              employee_id,
              company_id: decoded.company_id,
              month,
              year,
            },
            findOne: true,
          })

        if (existingRatingFeedback) {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.already_exist, {
              ":data": "Rating Feedback for this month and year",
            }),
          )
        }

        ratingFeedback = new MisReportRatingFeedback()
        ratingFeedback.company_id = decoded.company_id
        ratingFeedback.employee_id = employee_id
        ratingFeedback.month = month
        ratingFeedback.year = year
        ratingFeedback.business_feedback = business_feedback
        ratingFeedback.technical_feedback = technical_feedback
        ratingFeedback.personal_feedback = personal_feedback
        ratingFeedback.business_rating = business_rating || 0
        ratingFeedback.technical_rating = technical_rating || 0
        ratingFeedback.personal_rating = personal_rating || 0
        ratingFeedback.average_rating = average_rating

        if (decoded?.user_id) ratingFeedback.action_by_id = decoded.user_id

        if (decoded?.employee_id)
          ratingFeedback.created_by = decoded.employee_id
      }

      const savedRatingFeedback =
        await this.misReportRatingFeedbackRepository.save(ratingFeedback)

      const misReport = (await this.misReportRepository.getByParams({
        where: {
          id: report_id,
          employee_id: employee_id,
          company_id: decoded.company_id,
          month,
          year,
        },
        findOne: true,
      })) as any

      if (!misReport) {
        return failureResponse(
          code.ERROR,
          errorMessage(messageKey.not_found, { ":data": "MIS Report" }),
        )
      }

      misReport.status = "submitted"

      await this.misReportRepository.save(misReport)

      // Handle KPI ratings
      if (kpi_ratings && kpi_ratings.length > 0) {
        // Delete existing KPI ratings for this rating feedback record
        await this.businessKpiRatingEntityRepository
          .createQueryBuilder()
          .delete()
          .from(BusinessKpiRating)
          .where("rating_id = :ratingId", { ratingId: savedRatingFeedback.id })
          .andWhere("employee_id = :employeeId", { employeeId: employee_id })
          .execute()

        const businessKpiRatings: any = []

        for (const kpiRating of kpi_ratings) {
          const businessKpiRating = new BusinessKpiRating()
          businessKpiRating.kpi_id = kpiRating.kpi_id
          businessKpiRating.rating_id = savedRatingFeedback.id
          businessKpiRating.employee_id = employee_id
          businessKpiRating.rating = kpiRating.rating
          businessKpiRating.department_id = kpiRating.department_id
          businessKpiRating.month = kpiRating.month
          businessKpiRating.year = kpiRating.year

          if (decoded?.user_id) {
            businessKpiRating.created_by = decoded.user_id
          }

          businessKpiRatings.push(businessKpiRating)
        }

        if (businessKpiRatings.length > 0) {
          await this.businessKpiRatingRepository.save(businessKpiRatings)
        }
      }

      return successResponse(
        code.SUCCESS,
        successMessage(id ? messageKey.data_update : messageKey.data_add, {
          ":data": "MIS Action",
        }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async getMisActionDetails(
    employeeId: number,
    token: string,
    month?: number,
    year?: number,
  ) {
    try {
      const decoded = verifyJwtToken(token)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      // Get MIS report details
      const queryBuilder = this.misReportEntityRepository
        .createQueryBuilder("mis_reports")
        .leftJoin(
          "employees",
          "employee",
          "employee.id = mis_reports.employee_id",
        )
        .leftJoin(
          "departments",
          "department",
          "department.id = employee.department_id",
        )
        .leftJoin(
          "departments",
          "subDepartment",
          "subDepartment.id = employee.sub_department_id",
        )
        .leftJoin(
          "mis_report_rating_feedbacks",
          "feedback",
          "feedback.employee_id = mis_reports.employee_id AND feedback.month = mis_reports.month AND feedback.year = mis_reports.year",
        )
        .leftJoin("users", "actionBy", "actionBy.id = feedback.action_by_id")
        .where("mis_reports.employee_id = :employeeId", { employeeId })
        .andWhere("mis_reports.company_id = :companyId", {
          companyId: decoded.company_id,
        })
        .andWhere("mis_reports.status IN (:...statuses)", {
          statuses: ["pending", "submitted"],
        })

      // // Add month filter if provided
      if (month) {
        queryBuilder.andWhere("mis_reports.month = :month", { month })
      }

      // Add year filter if provided
      if (year) {
        queryBuilder.andWhere("mis_reports.year = :year", { year })
      }

      const misReport = await queryBuilder
        .select([
          "employee.id as employee_id",
          "employee.first_name as employee_first_name",
          "employee.last_name as employee_last_name",
          "employee.sub_department_id as employee_sub_department_id",
          "department.id as department_id",
          "department.name as department_name",
          "subDepartment.id as sub_department_id",
          "subDepartment.name as sub_department_name",
          "mis_reports.id as report_id",
          "mis_reports.business_tasks as business_tasks",
          "mis_reports.technical_tasks as technical_tasks",
          "mis_reports.personal_tasks as personal_tasks",
          "mis_reports.month as month",
          "mis_reports.year as year",
          "feedback.id as feedback_id",
          "feedback.business_feedback as business_feedback",
          "feedback.technical_feedback as technical_feedback",
          "feedback.personal_feedback as personal_feedback",
          "feedback.business_rating as business_rating",
          "feedback.technical_rating as technical_rating",
          "feedback.personal_rating as personal_rating",
          "feedback.average_rating as average_rating",
          "actionBy.id as action_by_id",
          "actionBy.first_name as action_by_first_name",
          "actionBy.last_name as action_by_last_name",
        ])
        .getRawOne()

      if (!misReport) {
        return failureResponse(
          code.ERROR,
          errorMessage(messageKey.data_not_found),
        )
      }

      // Get all KPIs for the employee's department
      const departmentKpis = await this.misReportEntityRepository.query(
        `
        SELECT DISTINCT k.id, k.name
        FROM mis_business_kpis k
        INNER JOIN business_kpis_departments bkd ON k.id = bkd.kpi_id
        WHERE bkd.department_id = $1 AND k.company_id = $2
        ORDER BY k.name
      `,
        [
          misReport?.employee_sub_department_id
            ? misReport?.employee_sub_department_id
            : misReport.department_id,
          decoded.company_id,
        ],
      )

      // Get existing KPI ratings for this employee and feedback
      const existingRatings = new Map()
      if (misReport.feedback_id) {
        const kpiRatingQueryBuilder = this.businessKpiRatingEntityRepository
          .createQueryBuilder("business_kpis_ratings")
          .where("business_kpis_ratings.employee_id = :employeeId", {
            employeeId,
          })
          .andWhere("business_kpis_ratings.rating_id = :ratingId", {
            ratingId: misReport.feedback_id,
          })

        // Add month and year filters to KPI ratings if provided
        if (month) {
          kpiRatingQueryBuilder.andWhere(
            "business_kpis_ratings.month = :month",
            { month },
          )
        }
        if (year) {
          kpiRatingQueryBuilder.andWhere("business_kpis_ratings.year = :year", {
            year,
          })
        }

        const kpiRatings = await kpiRatingQueryBuilder
          .select([
            "business_kpis_ratings.kpi_id as kpi_id",
            "business_kpis_ratings.rating as rating",
          ])
          .getRawMany()

        kpiRatings.forEach((rating: any) => {
          existingRatings.set(rating.kpi_id, rating.rating)
        })
      }

      // Map all department KPIs with their ratings (0 if no rating exists)
      const kpiRatingsWithDefaults = []

      if (misReport.feedback_id) {
        departmentKpis.map((kpi: any) => {
          if (existingRatings.get(kpi.id)) {
            kpiRatingsWithDefaults.push({
              kpi_name: kpi.name,
              id: kpi.id,
              rating: Number(existingRatings.get(kpi.id)) || 0,
            })
          }
        })
      } else {
        departmentKpis.map((kpi: any) => {
          kpiRatingsWithDefaults.push({
            kpi_name: kpi.name,
            id: kpi.id,
            rating: Number(existingRatings.get(kpi.id)) || 0,
          })
        })
      }

      const data: any = {
        employee: {
          id: misReport.employee_id,
          first_name: misReport.employee_first_name,
          last_name: misReport.employee_last_name,
          department: {
            id: misReport.department_id,
            name: misReport.department_name,
          },
          subDepartment: misReport?.employee_sub_department_id
            ? {
                id: misReport.sub_department_id,
                name: misReport.sub_department_name,
              }
            : null,
        },
        feedback_id: misReport.feedback_id,
        report_id: misReport.report_id,
        business_tasks: misReport.business_tasks,
        technical_tasks: misReport.technical_tasks,
        personal_tasks: misReport.personal_tasks,
        month: moment()
          .month(misReport.month - 1)
          .format("MMMM"),
        year: misReport.year,
        business_feedback: misReport.business_feedback,
        technical_feedback: misReport.technical_feedback,
        personal_feedback: misReport.personal_feedback,
        business_rating: Number(misReport.business_rating) || 0,
        technical_rating: Number(misReport.technical_rating) || 0,
        personal_rating: Number(misReport.personal_rating) || 0,
        average_rating: Number(misReport.average_rating) || 0,
        kpi_ratings: kpiRatingsWithDefaults,
        action_by: misReport.action_by_id
          ? {
              id: misReport.action_by_id,
              first_name: misReport.action_by_first_name,
              last_name: misReport.action_by_last_name,
            }
          : null,
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "MIS Action Details",
        }),
        data,
      )
    } catch (error) {
      console.log(error)

      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }
}
