import { Injectable } from "@nestjs/common"
import { CreateClientsCompanyDto } from "../dto/create-clients-company.dto"
import { UpdateClientsCompanyDto } from "../dto/update-clients-company.dto"
import { ClientCompanyRepository } from "../repositories/clients-companies.repository"
import {
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { failureResponse, successResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import { messageKey } from "src/constants/message-keys"
import { ClientsCompany } from "../entities/clients-company.entity"
import { ClientTypeRepository } from "../../client-type/repositories/client-type.repository"
import { CityRepository } from "../../city/repositories/city.repository"
import { StateRepository } from "../../state/repositories/state.repository"
import { ClientsCompaniesFilterDto } from "../dto/filter-clients-companies.dto"
import { ConfigService } from "@nestjs/config"
import { CreateInvoiceSettingsDto } from "../dto/create-invoice-settings.dto"
import { InvoiceSettings } from "../entities/invoice-settings.entity"
import { UpdateInvoiceSettingsDto } from "../dto/update-invoice-settings.dto"
import { InvoiceSettingsRepository } from "../repositories/invoice-settings.repository"
import moment from "moment"
import { ClientCompanyContractRepository } from "src/modules/client-contract/repositories/client-contarct.repository"

@Injectable()
export class ClientsCompaniesService {
  constructor(
    private readonly clientsCompaniesRepository: ClientCompanyRepository,
    private readonly clientTypeRepository: ClientTypeRepository,
    private readonly cityRepository: CityRepository,
    private readonly stateRepository: StateRepository,
    private readonly configService: ConfigService,
    private readonly invoiceSettingsRepository: InvoiceSettingsRepository,
    private readonly clientCompanyContractRepository: ClientCompanyContractRepository,
  ) {}

  async create(createClientsCompanyDto: CreateClientsCompanyDto) {
    const clientType = await this.clientTypeRepository.getByParams({
      where: {
        id: createClientsCompanyDto.client_type_id,
      },
      findOne: true,
    })

    if (isEmpty(clientType)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, {
          ":data": "Client Type",
        }),
      )
    }

    if (
      !isEmpty(createClientsCompanyDto?.city_id) &&
      createClientsCompanyDto?.city_id !== 0
    ) {
      const isCityExist: any = await this.cityRepository.getByParams({
        where: {
          id: createClientsCompanyDto.city_id,
        },
        findOne: true,
      })

      if (isEmpty(isCityExist)) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.data_not_found, {
            ":data": "City",
          }),
        )
      }

      if (isCityExist.state_id !== createClientsCompanyDto.state_id) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.data_not_found, {
            ":data": "City and state do not match",
          }),
        )
      }

      const isStateExist = await this.stateRepository.getByParams({
        where: {
          id: createClientsCompanyDto.state_id,
        },
        findOne: true,
      })

      if (isEmpty(isStateExist)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.data_not_found, {
            ":data": "State",
          }),
        )
      }
    }

    if (
      await this.checkDataExist(
        createClientsCompanyDto?.company_name,
        "company_name",
      )
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Company name",
        }),
      )
    }

    if (await this.checkDataExist(createClientsCompanyDto?.email, "email")) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Email",
        }),
      )
    }

    if (
      await this.checkDataExist(
        createClientsCompanyDto?.phone_number,
        "phone_number",
      )
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Phone number",
        }),
      )
    }

    const clientCompany = new ClientsCompany()

    Object.assign(clientCompany, createClientsCompanyDto)

    const clientCompanyData =
      await this.clientsCompaniesRepository.save(clientCompany)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_add, { ":data": "Client Company" }),
      clientCompanyData,
    )
  }

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

    const {
      search,
      limit,
      skip,
      country_id,
      client_type_id,
      is_medical_patient,
    } = filter || {}

    const startOfCurrentMonth = moment().startOf("month").toDate()
    const endOfCurrentMonth = moment().endOf("month").toDate()

    const queryParams: any = {
      take: limit || defaultPagination.take,
      skip: skip || defaultPagination.skip,
      orderBy: { created_at: "DESC" },
      select: [
        "id",
        "company_name",
        "address_line_1",
        "address_line_2",
        "city_id",
        "state_id",
        "country_id",
        "zip_code",
        "phone_number",
        "country_code",
        "email",
        "website_url",
        "is_medical_patient",
        "client_type_id",
        "status",
        "created_at",
      ],
      relations: [
        "city:id,name",
        "state:id,name",
        "country:id,name",
        "client_type:id,name",
        "client_contacts:id,first_name,last_name",
        "contracts:id,status,start_date,end_date",
      ],
      where: {},
    }

    // Search logic
    if (search) {
      queryParams.search = {
        company_name: search,
        phone_number: search,
        email: search,
      }
    }

    // Filter logic
    if (country_id) {
      queryParams.where.country_id = country_id
    }

    if (client_type_id) {
      queryParams.where.client_type_id = client_type_id
    }

    if (!isEmpty(is_medical_patient)) {
      queryParams.where.is_medical_patient =
        typeof is_medical_patient === "string"
          ? is_medical_patient === "true"
          : Boolean(is_medical_patient)
    }

    // Execute query
    const companies = (await this.clientsCompaniesRepository.getByParams(
      queryParams,
    )) as any

    companies.data = companies.data.map((company) => {
      const activeInCurrentMonth = (company.contracts || []).filter(
        (c: { start_date: Date; end_date: Date; status: string }) =>
          c.status === "active" &&
          moment(c.start_date).isSameOrBefore(endOfCurrentMonth) &&
          moment(c.end_date).isSameOrAfter(startOfCurrentMonth),
      )
      const singleActiveContract = activeInCurrentMonth[0] ?? null

      return {
        ...company,
        contracts: singleActiveContract ? [singleActiveContract] : [],
        client_contact_count: company.client_contacts?.length || 0,
      }
    })

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Clients Companies",
      }),
      companies,
    )
  }

  async findOne(id: number) {
    const clientCompany = await this.clientsCompaniesRepository.getByParams({
      where: { id },
      findOne: true,
      select: [
        "id",
        "company_name",
        "address_line_1",
        "address_line_2",
        "city_id",
        "state_id",
        "country_id",
        "zip_code",
        "phone_number",
        "country_code",
        "email",
        "website_url",
        "is_medical_patient",
        "client_type_id",
        "status",
        "created_at",
      ],
      relations: [
        "city:id,name",
        "state:id,name",
        "country:id,name",
        "client_type:id,name",
      ],
      relationCount: ["client_contacts"],
    })

    if (isEmpty(clientCompany)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Clients Company" }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Clients Company",
      }),
      clientCompany,
    )
  }

  async update(id: number, updateClientsCompanyDto: UpdateClientsCompanyDto) {
    const clientCompany = await this.clientsCompaniesRepository.getByParams({
      where: { id },
      findOne: true,
    })

    if (isEmpty(clientCompany)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Clients Company" }),
      )
    }

    if (
      await this.checkDataExist(
        updateClientsCompanyDto?.company_name,
        "company_name",
        id,
      )
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Company name",
        }),
      )
    }

    if (
      await this.checkDataExist(updateClientsCompanyDto?.email, "email", id)
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, { ":data": "Email" }),
      )
    }

    if (
      await this.checkDataExist(
        updateClientsCompanyDto?.phone_number,
        "phone_number",
        id,
      )
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Phone number",
        }),
      )
    }

    if (
      !isEmpty(updateClientsCompanyDto?.city_id) &&
      updateClientsCompanyDto?.city_id !== 0
    ) {
      const isCityExist: any = await this.cityRepository.getByParams({
        where: { id: updateClientsCompanyDto.city_id },
        findOne: true,
      })

      if (isEmpty(isCityExist)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.validation_error, { ":data": "City" }),
        )
      }

      if (isCityExist.state_id !== updateClientsCompanyDto.state_id) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.data_not_found, {
            ":data": "City and state do not match",
          }),
        )
      }

      const isStateExist = await this.stateRepository.getByParams({
        where: { id: updateClientsCompanyDto.state_id },
        findOne: true,
      })

      if (isEmpty(isStateExist)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.data_not_found, { ":data": "State" }),
        )
      }
    }

    const condition = { id }

    const companyEntity = new ClientsCompany()

    Object.assign(companyEntity, updateClientsCompanyDto)

    const companyData = await this.clientsCompaniesRepository.save(
      companyEntity,
      condition,
    )

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

  async remove(id: number) {
    const clientCompany = await this.clientsCompaniesRepository.getByParams({
      where: { id },
      findOne: true,
    })
    if (isEmpty(clientCompany)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Clients Company" }),
      )
    }
    await this.clientsCompaniesRepository.remove({ id }, null, false)
    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_removed, { ":data": "Clients Company" }),
    )
  }

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

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

    const queryParams: any = {
      take: limit || defaultPagination.take,
      skip: skip || defaultPagination.skip,
      orderBy: { company_name: "ASC" },
      select: ["id", "company_name", "is_medical_patient"],
    }

    if (search) {
      queryParams.search = {
        company_name: search,
      }
    }

    const companies =
      await this.clientsCompaniesRepository.getByParams(queryParams)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Clients Companies",
      }),
      companies,
    )
  }

  private async checkDataExist(
    value: string,
    field: string,
    excludeId?: number,
  ): Promise<boolean> {
    const whereCondition: any = {}
    const whereLikeCondition: any = {}
    whereLikeCondition[field] = value
    if (excludeId) {
      whereCondition["id"] = { not: excludeId }
    }
    const company = await this.clientsCompaniesRepository.getByParams({
      where: whereCondition,
      whereLower: whereLikeCondition,
      findOne: true,
    })
    return !isEmpty(company)
  }

  // InvoiceSettings CRUD operations
  async createInvoiceSettings(
    createInvoiceSettingsDto: CreateInvoiceSettingsDto,
  ) {
    try {
      // Validate client exists if clientId is provided
      if (createInvoiceSettingsDto.client_id) {
        const client = await this.clientsCompaniesRepository.getByParams({
          where: { id: createInvoiceSettingsDto.client_id },
          findOne: true,
        })

        if (isEmpty(client)) {
          return failureResponse(
            code.VALIDATION,
            errorMessage(messageKey.data_not_found, { ":data": "Client" }),
          )
        }
      }

      const invoiceSettings = new InvoiceSettings()
      Object.assign(invoiceSettings, createInvoiceSettingsDto)

      const savedInvoiceSettings =
        await this.invoiceSettingsRepository.save(invoiceSettings)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_add, { ":data": "Invoice Settings" }),
        savedInvoiceSettings,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async findOneInvoiceSettings(id: number) {
    try {
      const invoiceSettings = await this.invoiceSettingsRepository.getByParams({
        where: { id },
        findOne: true,
      })

      if (isEmpty(invoiceSettings)) {
        return failureResponse(
          code.BAD_REQUEST,
          errorMessage(messageKey.data_not_found, {
            ":data": "Invoice Settings",
          }),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.detail_found, {
          ":data": "Invoice Settings",
        }),
        invoiceSettings,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async findOneInvoiceSettingsFromCustomer(id: number) {
    try {
      const invoiceSettings = await this.invoiceSettingsRepository.getByParams({
        where: { client_id: id },
        findOne: true,
      })

      if (isEmpty(invoiceSettings)) {
        return failureResponse(
          code.BAD_REQUEST,
          errorMessage(messageKey.data_not_found, {
            ":data": "Invoice Settings",
          }),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.detail_found, {
          ":data": "Invoice Settings",
        }),
        invoiceSettings,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async updateInvoiceSettings(id: number, updateDto: UpdateInvoiceSettingsDto) {
    try {
      const invoiceSettings = (await this.invoiceSettingsRepository.getByParams(
        {
          where: { id },
          findOne: true,
        },
      )) as InvoiceSettings

      if (isEmpty(invoiceSettings)) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.data_not_found, {
            ":data": "Invoice Settings",
          }),
        )
      }

      if (updateDto.client_id) {
        const client = await this.clientsCompaniesRepository.getByParams({
          where: { id: updateDto.client_id },
          findOne: true,
        })

        if (isEmpty(client)) {
          return failureResponse(
            code.VALIDATION,
            errorMessage(messageKey.data_not_found, { ":data": "client" }),
          )
        }
      }

      Object.assign(invoiceSettings, updateDto)
      const updatedInvoiceSettings =
        await this.invoiceSettingsRepository.save(invoiceSettings)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, { ":data": "Invoice Settings" }),
        updatedInvoiceSettings,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async removeInvoiceSettings(id: number) {
    try {
      const invoiceSettings = await this.invoiceSettingsRepository.getByParams({
        where: { id },
        findOne: true,
      })

      if (isEmpty(invoiceSettings)) {
        return failureResponse(
          code.BAD_REQUEST,
          errorMessage(messageKey.data_not_found, {
            ":data": "Invoice Settings",
          }),
        )
      }

      await this.invoiceSettingsRepository.remove({ id })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_removed, {
          ":data": "Invoice Settings",
        }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }
}
