import { Injectable } from "@nestjs/common"
import { CreateHospitalContactDto } from "../dto/create-hospital-contact.dto"
import { UpdateHospitalContactDto } from "../dto/update-hospital-contact.dto"
import { HospitalContactRepository } from "../repositories/hospital-contact.repository"
import { HospitalRepository } from "../../hospitals/repositories/hospital.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 { HospitalContact } from "../entities/hospital-contact.entity"
import { HospitalContactFilterDto } from "../dto/filter-hospital-contact.dto"
import { ConfigService } from "@nestjs/config"

@Injectable()
export class HospitalContactsService {
  constructor(
    private readonly hospitalContactRepository: HospitalContactRepository,
    private readonly hospitalRepository: HospitalRepository,
    private readonly configService: ConfigService,
  ) {}

  async create(createHospitalContactDto: CreateHospitalContactDto) {
    const isHospitalExists = await this.hospitalRepository.getByParams({
      where: {
        id: createHospitalContactDto.hospital_id,
      },
      findOne: true,
    })

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

    const isPrimaryContactExists =
      await this.hospitalContactRepository.getByParams({
        where: {
          hospital_id: createHospitalContactDto.hospital_id,
          is_primary: createHospitalContactDto.is_primary,
        },
        findOne: true,
      })

    if (
      !isEmpty(isPrimaryContactExists) &&
      createHospitalContactDto.is_primary
    ) {
      return failureResponse(
        code.BAD_REQUEST,
        validationMessage(messageKey.already_exist, {
          ":data": "Hospital primary contact",
        }),
      )
    }

    const isContactExists = await this.hospitalContactRepository.getByParams({
      where: {
        hospital_id: createHospitalContactDto.hospital_id,
        email: createHospitalContactDto.email,
      },
      findOne: true,
    })

    if (!isEmpty(isContactExists)) {
      return failureResponse(
        code.BAD_REQUEST,
        validationMessage(messageKey.already_exist, {
          ":data": "Email",
        }),
      )
    }

    const isPhoneNumberExists =
      await this.hospitalContactRepository.getByParams({
        where: {
          hospital_id: createHospitalContactDto.hospital_id,
          phone_number: createHospitalContactDto.phone_number,
        },
        findOne: true,
      })

    if (!isEmpty(isPhoneNumberExists)) {
      return failureResponse(
        code.BAD_REQUEST,
        validationMessage(messageKey.already_exist, {
          ":data": "Phone Number",
        }),
      )
    }

    const hospitalContact = new HospitalContact()

    Object.assign(hospitalContact, createHospitalContactDto)

    await this.hospitalContactRepository.save(hospitalContact)

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

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

    const validSortColumns = [
      "created_at",
      "updated_at",
      "name",
      "email",
      "phone_number",
      "position",
      "department",
      "is_primary",
    ] as const

    const sortBy =
      (hospitalContactFilterDto.sortBy as (typeof validSortColumns)[number]) ||
      "is_primary"
    const sortOrder = hospitalContactFilterDto.sortOrder || "DESC"

    const params: any = {
      where: {
        hospital_id: hospitalContactFilterDto.hospital_id,
      },
      take: Number(hospitalContactFilterDto.limit) || defaultPagination.take,
      skip: Number(hospitalContactFilterDto.skip) || defaultPagination.skip,
      orderBy: {
        [validSortColumns.includes(sortBy) ? sortBy : "is_primary"]: sortOrder,
      },
      select: [
        "id",
        "name",
        "position",
        "department",
        "email",
        "country_code",
        "phone_number",
        "is_primary",
        "hospital_id",
        "created_at",
        "updated_at",
      ],
    }

    if (hospitalContactFilterDto.search) {
      const search = hospitalContactFilterDto.search
      params.search = {
        name: search,
        email: search,
        phone_number: search,
        position: search,
        department: search,
      }
    }

    const hospitalContacts =
      await this.hospitalContactRepository.getByParams(params)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Hospital Contacts",
      }),
      hospitalContacts,
    )
  }

  async findOne(id: number) {
    const hospitalContact = await this.hospitalContactRepository.getByParams({
      where: { id },
      findOne: true,
    })
    if (isEmpty(hospitalContact)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Hospital Contact",
        }),
      )
    }
    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Hospital Contact" }),
      hospitalContact,
    )
  }

  async update(id: number, updateHospitalContactDto: UpdateHospitalContactDto) {
    const hospitalContactData =
      await this.hospitalContactRepository.getByParams({
        where: { id },
        findOne: true,
      })

    if (isEmpty(hospitalContactData)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Hospital Contact",
        }),
      )
    }

    if (updateHospitalContactDto.is_primary) {
      const isPrimaryContactExists =
        await this.hospitalContactRepository.getByParams({
          where: {
            hospital_id: updateHospitalContactDto.hospital_id,
            is_primary: updateHospitalContactDto.is_primary,
          },
          whereNotIn: {
            id: [id],
          },
          findOne: true,
        })

      if (!isEmpty(isPrimaryContactExists)) {
        return failureResponse(
          code.BAD_REQUEST,
          validationMessage(messageKey.already_exist, {
            ":data": "Hospital primary contact",
          }),
        )
      }
    }

    const isContactExists = await this.hospitalContactRepository.getByParams({
      where: {
        hospital_id: updateHospitalContactDto.hospital_id,
        email: updateHospitalContactDto.email,
      },
      whereNotIn: {
        id: [id],
      },
      findOne: true,
    })

    if (!isEmpty(isContactExists)) {
      return failureResponse(
        code.BAD_REQUEST,
        validationMessage(messageKey.already_exist, {
          ":data": "Email",
        }),
      )
    }

    const isPhoneNumberExists =
      await this.hospitalContactRepository.getByParams({
        where: {
          hospital_id: updateHospitalContactDto.hospital_id,
          phone_number: updateHospitalContactDto.phone_number,
        },
        whereNotIn: {
          id: [id],
        },
        findOne: true,
      })

    if (!isEmpty(isPhoneNumberExists)) {
      return failureResponse(
        code.BAD_REQUEST,
        validationMessage(messageKey.already_exist, {
          ":data": "Phone Number",
        }),
      )
    }

    const condition = {
      id: id,
    }
    const hospitalContact = new HospitalContact()

    Object.assign(hospitalContact, updateHospitalContactDto)

    await this.hospitalContactRepository.save(hospitalContact, condition)

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

  async remove(id: number) {
    const hospitalContact = await this.hospitalContactRepository.getByParams({
      where: { id },
      findOne: true,
    })
    if (isEmpty(hospitalContact)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Hospital Contact",
        }),
      )
    }
    await this.hospitalContactRepository.remove({ id: Number(id) })
    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_removed, { ":data": "Hospital Contact" }),
    )
  }
}
