import { Injectable } from "@nestjs/common"
import {
  failureResponse,
  successResponse,
} from "../../common/response/response"
import { code } from "../../common/response/response.code"
import { messageKey } from "../../constants/message-keys"
import {
  convertLocalToUtc,
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "../../utils/helpers"
import { verifyJwtToken } from "../../utils/jwt"
import { EmployeeRepository } from "../employees/repositories/employee.repository"
import { CreateEmployeeFlagDto } from "./dto/create-employee-flag.dto"
import { MyFlagsByEmployeeIdFilterDto } from "./dto/my-flags-by-employee-id-filter.dto"
import { EmployeeFlag } from "./entities/employee-flag.entity"
import { EmployeeFlagRepository } from "./repositories/employee-flag.repository"
import { FlagRepository } from "./repositories/flag.repository"

@Injectable()
export class EmployeeFlagsService {
  constructor(
    private readonly employeeFlagRepository: EmployeeFlagRepository,
    private readonly employeeRepository: EmployeeRepository,
    private readonly flagRepository: FlagRepository,
  ) {}

  async create(createEmployeeFlagDto: CreateEmployeeFlagDto, token: string) {
    const decoded = verifyJwtToken(token)

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

    try {
      const { flag_id, reason, resolve_at, employee_id } = createEmployeeFlagDto

      const companyId = decoded?.company_id
      const employeeId = decoded?.employee_id

      const flagDetail: any = await this.flagRepository.getByParams({
        where: {
          id: flag_id,
        },
        findOne: true,
      })

      if (isEmpty(flagDetail)) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, { ":data": "Flag" }),
        )
      }

      const employeeDetail: any = await this.employeeRepository.getByParams({
        where: {
          id: Number(employee_id),
        },
        findOne: true,
      })

      if (isEmpty(employeeDetail)) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, { ":data": "Employee" }),
        )
      }

      const employeeFlag = new EmployeeFlag()

      employeeFlag.flag_id = flag_id
      employeeFlag.employee_id = employeeDetail?.id
      employeeFlag.company_id = companyId
      employeeFlag.reason = reason
      employeeFlag.resolve_at = convertLocalToUtc(new Date(resolve_at))

      if (employeeId) employeeFlag.action_by_id = employeeId

      await this.employeeFlagRepository.save(employeeFlag)

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

  async findAllFlags() {
    try {
      const data: any = await this.flagRepository.getByParams({
        select: ["id", "name"],
      })

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

  async findAll(query: MyFlagsByEmployeeIdFilterDto, token: string) {
    const decoded = verifyJwtToken(token)

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

    try {
      const data: any = await this.getEmployeeFlagDetails(query, token)

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

  async findFlagsByEmployeeId(
    employeeId: number,
    query: MyFlagsByEmployeeIdFilterDto,
    token: string,
  ) {
    const decoded = verifyJwtToken(token)

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

    try {
      const data: any = await this.getEmployeeFlagDetails(
        query,
        token,
        employeeId,
      )

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

  async findOne(id: number) {
    try {
      const findEmployeeFlagData =
        await this.employeeFlagRepository.getByParams({
          findOne: true,
          where: {
            id,
          },
          relations: ["flags:id,name", "actionBy:id,first_name,last_name"],
          select: [
            "id",
            "flags",
            "actionBy",
            "resolve_at",
            "created_at",
            "status",
            "reason",
          ],
        })

      if (!findEmployeeFlagData) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.data_not_found, {
            ":data": "Employee Flag Data",
          }),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "Employee Flag Data",
        }),
        findEmployeeFlagData as any,
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

  async markAsResolved(id: number) {
    try {
      const findEmployeeFlagData: any =
        await this.employeeFlagRepository.getByParams({
          findOne: true,
          where: {
            id,
          },
        })

      if (!findEmployeeFlagData) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.data_not_found, {
            ":data": "Employee Flag Data",
          }),
        )
      }

      if (findEmployeeFlagData.status === "resolved") {
        return failureResponse(
          code.VALIDATION,
          validationMessage("You have already resolved this flag."),
        )
      }

      findEmployeeFlagData.status = "resolved"

      await this.employeeFlagRepository.save(findEmployeeFlagData)

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

  private async getEmployeeFlagDetails(
    query: MyFlagsByEmployeeIdFilterDto,
    token: string,
    employeeId?: number,
  ): Promise<any> {
    const decoded = verifyJwtToken(token)

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

    try {
      const {
        page = 1,
        limit = 10,
        search,
        status,
        month,
        year,
        action_by_id,
        flag_id,
      } = query

      const skip = (page - 1) * limit
      const take = parseInt(limit.toString())

      const whereConditions: any = {}
      const searchConditions: any = {}

      // Add filters
      if (employeeId) {
        whereConditions.employee_id = employeeId
      }

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

      if (action_by_id) {
        whereConditions.action_by_id = action_by_id
      }

      if (flag_id) {
        whereConditions.flag_id = flag_id
      }

      if (status) {
        whereConditions.status = status
      }

      if (month && year) {
        const now = new Date()

        const selectedMonth = !isEmpty(month)
          ? Number(month)
          : now.getMonth() + 1

        const selectedYear = !isEmpty(year) ? Number(year) : now.getFullYear()

        // Start of month
        const startDate = new Date(selectedYear, selectedMonth - 1, 1)

        // End of month
        const endDate = new Date(selectedYear, selectedMonth, 0)

        whereConditions.created_at = {
          bt: {
            start: startDate,
            end: endDate,
          },
        }
      }

      if (search) {
        searchConditions["entity_employee.first_name"] = search
        searchConditions["entity_employee.last_name"] = search
      }

      const data: any = await this.employeeFlagRepository.getByParams({
        where: whereConditions,
        search: !isEmpty(searchConditions) ? searchConditions : undefined,
        take,
        skip,
        relations: [
          "flags:id,name",
          "actionBy:id,first_name,last_name",
          "employee:id,first_name,last_name.department:id,name",
        ],
        select: [
          "id",
          "employee",
          "actionBy",
          "resolve_at",
          "created_at",
          "status",
          "reason",
        ],
        orderBy: { created_at: "DESC" },
      })

      return data
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }
}
