import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"
import { InjectRepository } from "@nestjs/typeorm"
import { failureResponse, successResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import { messageKey } from "src/constants/message-keys"
import { errorMessage, isEmpty, successMessage } from "src/utils/helpers"
import { Repository } from "typeorm"
import { ClientCompanyContactRepository } from "../../client-company-contacts/repositories/client-company-contacts.repository"
import { PricingPlanRepository } from "../../plans/repositories/plan.repository"
import { CreateClientCompanyContractDto } from "../dto/create-client-contract.dto"
import { ClientCompanyContractFilterDto } from "../dto/filter-client-contract.dto"
import { UpdateClientCompanyContractDto } from "../dto/update-client-contract.dto"
import { ClientCompanyContract } from "../entities/client-contract.entity"
import { ClientCompanyContractRepository } from "../repositories/client-contarct.repository"
import moment from "moment"

@Injectable()
export class ClientContractService {
  constructor(
    private readonly clientCompanyContractsRepository: ClientCompanyContractRepository,
    private readonly clientCompanyContactRepository: ClientCompanyContactRepository,
    private readonly paymentPlanRepository: PricingPlanRepository,
    private readonly configService: ConfigService,
    @InjectRepository(ClientCompanyContract)
    private readonly clientContactEntityRepository: Repository<ClientCompanyContract>,
  ) {}

  // async create(
  //   createContractDto: CreateClientCompanyContractDto,
  //   clientContractFile?: Express.Multer.File,
  // ) {
  //   const isPlanExist = await this.paymentPlanRepository.getByParams({
  //     where: { id: createContractDto.payment_plan_id },
  //     findOne: true,
  //   })
  //   if (isEmpty(isPlanExist)) {
  //     return failureResponse(
  //       code.DATA_NOT_FOUND,
  //       errorMessage(messageKey.data_not_found, { ":data": "Payment Plan" }),
  //     )
  //   }

  //   const isContactExist =
  //     (await this.clientCompanyContactRepository.getByParams({
  //       where: { id: createContractDto.authorized_contact_id },
  //       relations: ["client_company"],
  //       findOne: true,
  //     })) as any

  //   if (isEmpty(isContactExist)) {
  //     return failureResponse(
  //       code.DATA_NOT_FOUND,
  //       errorMessage(messageKey.data_not_found, {
  //         ":data": "Authorized Client Contact",
  //       }),
  //     )
  //   }

  //   const clientCompanyId = isContactExist.client_company.id

  //   const newStart = moment(createContractDto.start_date).format("YYYY-MM-DD")
  //   const newEnd = moment(createContractDto.end_date).format("YYYY-MM-DD")

  //   const activeContract = await this.clientContactEntityRepository
  //     .createQueryBuilder("contract")
  //     .leftJoin("contract.authorized_contact", "authorized_contact")
  //     .leftJoin("authorized_contact.client_company", "client_company")
  //     .where("client_company.id = :clientCompanyId", { clientCompanyId })
  //     .andWhere("contract.status = :status", { status: "active" })
  //     .getOne()

  //   if (activeContract) {
  //     const activeStart = moment(activeContract.start_date).format("YYYY-MM-DD")
  //     const activeEnd = moment(activeContract.end_date).format("YYYY-MM-DD")

  //     const isOverlapping =
  //       (activeStart <= newStart && activeEnd >= newStart) ||
  //       (activeStart <= newEnd && activeEnd >= newEnd) ||
  //       (newStart <= activeStart && newEnd >= activeEnd)

  //     if (isOverlapping) {
  //       return failureResponse(
  //         code.ERROR,
  //         errorMessage(messageKey.active_contract, { ":data": "Contract" }),
  //       )
  //     }

  //     if (moment(newStart).isAfter(activeEnd)) {
  //       createContractDto.status = "upcoming"
  //     } else {
  //       createContractDto.status = "active"
  //     }
  //   } else {
  //     createContractDto.status = "active"
  //   }

  //   if (createContractDto.end_date) {
  //     const today = moment().startOf("day")
  //     const endDate = moment(createContractDto.end_date).startOf("day")

  //     if (endDate.isBefore(today)) {
  //       createContractDto.status = "expired"
  //     } else {
  //       createContractDto.status = "active"
  //     }
  //   }

  //   const contract = new ClientCompanyContract()
  //   Object.assign(contract, createContractDto)

  //   if (clientContractFile) {
  //     const relativePath = clientContractFile.path
  //       .replace(/\\/g, "/")
  //       .replace(/^public\//, "")
  //     contract.contract_document = relativePath
  //   }

  //   await this.clientCompanyContractsRepository.save(contract)

  //   return successResponse(
  //     code.SUCCESS,
  //     successMessage(messageKey.data_add, { ":data": "Contract" }),
  //     contract,
  //   )
  // }

  async create(
    createContractDto: CreateClientCompanyContractDto,
    clientContractFile?: Express.Multer.File,
  ) {
    const isPlanExist = await this.paymentPlanRepository.getByParams({
      where: { id: createContractDto.payment_plan_id },
      findOne: true,
    })

    if (isEmpty(isPlanExist)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Payment Plan" }),
      )
    }

    let clientCompanyId: number

    // Only validate authorized_contact_id if it's provided
    if (createContractDto.authorized_contact_id) {
      const isContactExist =
        (await this.clientCompanyContactRepository.getByParams({
          where: { id: createContractDto.authorized_contact_id },
          relations: ["client_company"],
          findOne: true,
        })) as any

      if (isEmpty(isContactExist)) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, {
            ":data": "Authorized Client Contact",
          }),
        )
      }
      clientCompanyId = isContactExist.client_company.id
    } else {
      // If authorized_contact_id is not provided, use client_company_id from DTO
      clientCompanyId = createContractDto.client_company_id
    }

    const newStart = moment(createContractDto.start_date).format("YYYY-MM-DD")
    const newEnd = moment(createContractDto.end_date).format("YYYY-MM-DD")
    const today = moment().startOf("day")
    const startDate = moment(createContractDto.start_date).startOf("day")
    const endDate = moment(createContractDto.end_date).startOf("day")

    const activeContract = await this.clientContactEntityRepository
      .createQueryBuilder("contract")
      .leftJoin("contract.authorized_contact", "authorized_contact")
      .leftJoin("authorized_contact.client_company", "client_company")
      .where("client_company.id = :clientCompanyId", { clientCompanyId })
      .andWhere("contract.status = :status", { status: "active" })
      .getOne()

    if (activeContract) {
      const activeStart = moment(activeContract.start_date).format("YYYY-MM-DD")
      const activeEnd = moment(activeContract.end_date).format("YYYY-MM-DD")

      const isOverlapping =
        (activeStart <= newStart && activeEnd >= newStart) ||
        (activeStart <= newEnd && activeEnd >= newEnd) ||
        (newStart <= activeStart && newEnd >= activeEnd)

      if (isOverlapping) {
        return failureResponse(
          code.ERROR,
          errorMessage(messageKey.active_contract, { ":data": "Contract" }),
        )
      }

      // Check if the contract has already expired
      if (endDate.isBefore(today)) {
        createContractDto.status = "expired"
      }
      // If new contract starts after active contract ends
      else if (moment(newStart).isAfter(activeEnd)) {
        createContractDto.status = "upcoming"
      }
      // If start date is today or in the past (but not expired)
      else if (startDate.isSameOrBefore(today)) {
        createContractDto.status = "active"
      }
      // If start date is in the future
      else {
        createContractDto.status = "upcoming"
      }
    } else {
      // No active contract exists
      // Check dates to determine status
      if (endDate.isBefore(today)) {
        createContractDto.status = "expired"
      } else if (startDate.isAfter(today)) {
        createContractDto.status = "upcoming"
      } else {
        createContractDto.status = "active"
      }
    }

    const contract = new ClientCompanyContract()
    Object.assign(contract, createContractDto)

    if (clientContractFile) {
      // Check if file was uploaded to R2
      if (
        (clientContractFile as any).storage === "R2" &&
        (clientContractFile as any).url
      ) {
        contract.contract_document = (clientContractFile as any).url
      } else {
        // Local storage - get relative path
        const relativePath = clientContractFile.path
          .replace(/\\/g, "/")
          .split("public/")[1]
        contract.contract_document = relativePath
      }
    }

    await this.clientCompanyContractsRepository.save(contract)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_add, { ":data": "Contract" }),
      contract,
    )
  }

  async findAll(filter: ClientCompanyContractFilterDto) {
    const defaultPagination = {
      take: this.configService.get<number>("APP.pagination.take"),
      skip: this.configService.get<number>("APP.pagination.skip"),
    }

    const { search, limit, skip, client_id } = filter || {}

    const qb = this.clientContactEntityRepository
      .createQueryBuilder("contract")
      .leftJoinAndSelect("contract.payment_plan", "payment_plan")
      .leftJoinAndSelect("contract.authorized_contact", "authorized_contact")
      .leftJoinAndSelect("authorized_contact.client_company", "client_company")
      .leftJoinAndSelect("contract.client_company", "direct_client_company") // Add direct relation

    if (client_id) {
      // Check both - contracts with authorized_contact OR direct client_company_id
      qb.andWhere(
        "(client_company.id IN (:...clientIds) OR direct_client_company.id IN (:...clientIds))",
        {
          clientIds: client_id.split(",").map((id) => Number(id)),
        },
      )
    }

    if (search) {
      qb.andWhere(
        `(
        CAST(contract.id AS TEXT) ILIKE :search OR
        contract.status ILIKE :search OR
        TO_CHAR(contract.start_date, 'YYYY-MM-DD') ILIKE :search OR
        TO_CHAR(contract.end_date, 'YYYY-MM-DD') ILIKE :search OR
        contract.description ILIKE :search OR
        contract.terms_and_conditions ILIKE :search OR
        authorized_contact.first_name ILIKE :search OR
        authorized_contact.last_name ILIKE :search OR
        authorized_contact.email ILIKE :search OR
        direct_client_company.company_name ILIKE :search
      )`,
        { search: `%${search}%` },
      )
    }

    qb.take(Number(limit) || defaultPagination.take)
    qb.skip(Number(skip) || defaultPagination.skip)
    qb.orderBy("contract.created_at", "DESC")

    qb.select([
      "contract.id",
      "contract.start_date",
      "contract.end_date",
      "contract.payment_plan_id",
      "contract.authorized_contact_id",
      "contract.description",
      "contract.payment_schedule",
      "contract.contract_document",
      "contract.terms_and_conditions",
      "contract.status",
      "contract.created_at",
      "contract.updated_at",
      "contract.client_company_id",

      "payment_plan.id",
      "payment_plan.name",

      "authorized_contact.id",
      "authorized_contact.first_name",
      "authorized_contact.last_name",
      "authorized_contact.email",

      "client_company.id",
      "client_company.company_name",

      "direct_client_company.id",
      "direct_client_company.company_name",
    ])

    const [data, count] = await qb.getManyAndCount()

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

  async findOne(id: number) {
    const contract = await this.clientCompanyContractsRepository.getByParams({
      where: { id },
      findOne: true,
      relations: [
        "payment_plan:id,name",
        "authorized_contact:id,first_name,last_name,email",
      ],
    })

    if (isEmpty(contract)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Contract" }),
      )
    }

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

  // async update(
  //   id: number,
  //   updateDto: UpdateClientCompanyContractDto,
  //   clientContractFile?: Express.Multer.File,
  // ) {
  //   const contract = await this.clientCompanyContractsRepository.getByParams({
  //     where: { id },
  //     findOne: true,
  //   })

  //   if (isEmpty(contract)) {
  //     return failureResponse(
  //       code.DATA_NOT_FOUND,
  //       errorMessage(messageKey.data_not_found, { ":data": "Contract" }),
  //     )
  //   }

  //   if (updateDto.payment_plan_id) {
  //     const isPlanExist = await this.paymentPlanRepository.getByParams({
  //       where: { id: updateDto.payment_plan_id },
  //       findOne: true,
  //     })

  //     if (isEmpty(isPlanExist)) {
  //       return failureResponse(
  //         code.DATA_NOT_FOUND,
  //         errorMessage(messageKey.data_not_found, { ":data": "Payment Plan" }),
  //       )
  //     }
  //   }

  //   if (updateDto.authorized_contact_id) {
  //     const isContactExist =
  //       await this.clientCompanyContactRepository.getByParams({
  //         where: { id: updateDto.authorized_contact_id },
  //         findOne: true,
  //       })
  //     if (isEmpty(isContactExist)) {
  //       return failureResponse(
  //         code.DATA_NOT_FOUND,
  //         errorMessage(messageKey.data_not_found, {
  //           ":data": "Authorized Client Contact",
  //         }),
  //       )
  //     }
  //   }

  //   // 👇 Auto update status using moment
  //   if (updateDto.end_date) {
  //     const today = moment().startOf("day")
  //     const endDate = moment(updateDto.end_date).startOf("day")

  //     if (endDate.isBefore(today)) {
  //       updateDto.status = "expired"
  //     } else {
  //       updateDto.status = "active"
  //     }
  //   }

  //   const contractEntity = new ClientCompanyContract()
  //   Object.assign(contractEntity, updateDto)

  //   if (clientContractFile) {
  //     const relativePath = clientContractFile.path
  //       .replace(/\\/g, "/")
  //       .replace(/^public\//, "")
  //     contractEntity.contract_document = relativePath
  //   }

  //   const data = await this.clientCompanyContractsRepository.save(
  //     contractEntity,
  //     {
  //       id,
  //     },
  //   )

  //   return successResponse(
  //     code.SUCCESS,
  //     successMessage(messageKey.data_update, { ":data": "Contract" }),
  //     data,
  //   )
  // }

  async update(
    id: number,
    updateDto: UpdateClientCompanyContractDto,
    clientContractFile?: Express.Multer.File,
  ) {
    const contract = await this.clientContactEntityRepository
      .createQueryBuilder("contract")
      .leftJoinAndSelect("contract.authorized_contact", "authorized_contact")
      .leftJoinAndSelect("authorized_contact.client_company", "client_company")
      .where("contract.id = :id", { id })
      .getOne()

    if (isEmpty(contract)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Contract" }),
      )
    }

    if (updateDto.payment_plan_id) {
      const isPlanExist = await this.paymentPlanRepository.getByParams({
        where: { id: updateDto.payment_plan_id },
        findOne: true,
      })

      if (isEmpty(isPlanExist)) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, { ":data": "Payment Plan" }),
        )
      }
    }

    // Only validate authorized_contact_id if it's provided and not empty
    let clientCompanyId: number
    if (updateDto.authorized_contact_id) {
      const isContactExist =
        (await this.clientCompanyContactRepository.getByParams({
          where: { id: updateDto.authorized_contact_id },
          relations: ["client_company"],
          findOne: true,
        })) as any
      if (isEmpty(isContactExist)) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, {
            ":data": "Authorized Client Contact",
          }),
        )
      }
      clientCompanyId = isContactExist.client_company.id
    } else if (contract.authorized_contact?.client_company) {
      clientCompanyId = contract.authorized_contact.client_company.id
    }

    // Check for active contracts if dates are being updated
    if (updateDto.start_date || updateDto.end_date) {
      const startDate = updateDto.start_date || contract.start_date
      const endDate = updateDto.end_date || contract.end_date
      const newStart = moment(startDate).format("YYYY-MM-DD")
      const newEnd = moment(endDate).format("YYYY-MM-DD")
      const today = moment().startOf("day")
      const startMoment = moment(startDate).startOf("day")
      const endMoment = moment(endDate).startOf("day")

      const activeContract = await this.clientContactEntityRepository
        .createQueryBuilder("contract")
        .leftJoin("contract.authorized_contact", "authorized_contact")
        .leftJoin("authorized_contact.client_company", "client_company")
        .where("client_company.id = :clientCompanyId", { clientCompanyId })
        .andWhere("contract.status = :status", { status: "active" })
        .andWhere("contract.id != :currentId", { currentId: id })
        .getOne()

      if (activeContract) {
        const activeStart = moment(activeContract.start_date).format(
          "YYYY-MM-DD",
        )
        const activeEnd = moment(activeContract.end_date).format("YYYY-MM-DD")

        const isOverlapping =
          (activeStart <= newStart && activeEnd >= newStart) ||
          (activeStart <= newEnd && activeEnd >= newEnd) ||
          (newStart <= activeStart && newEnd >= activeEnd)

        if (isOverlapping) {
          return failureResponse(
            code.ERROR,
            errorMessage(messageKey.active_contract, { ":data": "Contract" }),
          )
        }

        // Check if the contract has already expired
        if (endMoment.isBefore(today)) {
          updateDto.status = "expired"
        }
        // If new contract starts after active contract ends
        else if (moment(newStart).isAfter(activeEnd)) {
          updateDto.status = "upcoming"
        }
        // If start date is today or in the past (but not expired)
        else if (startMoment.isSameOrBefore(today)) {
          updateDto.status = "active"
        }
        // If start date is in the future
        else {
          updateDto.status = "upcoming"
        }
      } else {
        // No other active contract exists
        if (endMoment.isBefore(today)) {
          updateDto.status = "expired"
        } else if (startMoment.isAfter(today)) {
          updateDto.status = "upcoming"
        } else {
          updateDto.status = "active"
        }
      }
    } else {
      // 👇 Auto update status using moment (original code kept for non-date updates)
      if (updateDto.end_date) {
        const today = moment().startOf("day")
        const endDate = moment(updateDto.end_date).startOf("day")

        if (endDate.isBefore(today)) {
          updateDto.status = "expired"
        } else {
          updateDto.status = "active"
        }
      }
    }

    const contractEntity = new ClientCompanyContract()
    Object.assign(contractEntity, updateDto)

    if (clientContractFile) {
      // Check if file was uploaded to R2
      if (
        (clientContractFile as any).storage === "R2" &&
        (clientContractFile as any).url
      ) {
        contractEntity.contract_document = (clientContractFile as any).url
      } else {
        // Local storage - get relative path
        const relativePath = clientContractFile.path
          .replace(/\\/g, "/")
          .split("public/")[1]
        contractEntity.contract_document = relativePath
      }
    }

    const data = await this.clientCompanyContractsRepository.save(
      contractEntity,
      {
        id,
      },
    )

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, { ":data": "Contract" }),
      data,
    )
  }

  async remove(id: number) {
    const contract = await this.clientCompanyContractsRepository.getByParams({
      where: { id },
      findOne: true,
    })

    if (isEmpty(contract)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Contract" }),
      )
    }

    await this.clientCompanyContractsRepository.remove({ id })

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_removed, { ":data": "Contract" }),
    )
  }
}
