import { Injectable } from "@nestjs/common"
import { ClientCompanyRepository } from "../../clients-companies/repositories/clients-companies.repository"
import { HospitalRepository } from "../../hospitals/repositories/hospital.repository"
import { CustomerRepository } from "../../customers/repositories/customers.repository"
import { EscortRepository } from "../../escorts/repositories/escort.repository"
import { TripRepository } from "../../trips/repositories/trip.repository"
import { FleetManagementRepository } from "../../fleet-management/repositories/fleet-management.repository"
import { QueryParams } from "src/common/interfaces/query-params.interface"
import { ClientsCompany } from "../../clients-companies/entities/clients-company.entity"
import { Hospital } from "../../hospitals/entities/hospital.entity"
import { Customer } from "../../customers/entities/customer.entity"
import { Escort } from "../../escorts/entities/escort.entity"
import { Trip } from "../../trips/entities/trip.entity"
import { FleetManagement } from "../../fleet-management/entities/fleet-management.entity"
import { GetDashboardStatsDto } from "../dto/get-dashboard-stats.dto"
import { UserLoginRepository } from "src/modules/auth/repositories/user-login.repository"

@Injectable()
export class DashboardService {
  constructor(
    private clientCompanyRepository: ClientCompanyRepository,
    private hospitalRepository: HospitalRepository,
    private customerRepository: CustomerRepository,
    private escortRepository: EscortRepository,
    private tripRepository: TripRepository,
    private fleetManagementRepository: FleetManagementRepository,
    private readonly userLoginRepository: UserLoginRepository,
  ) {}

  /** When neither date is set, counts are not filtered by `created_at` (all records to date). */
  private createdAtRangeFromQuery(
    query: GetDashboardStatsDto,
  ): { gte?: Date; lte?: Date } | undefined {
    if (!query.startDate && !query.endDate) {
      return undefined
    }
    const range: { gte?: Date; lte?: Date } = {}
    if (query.startDate) {
      range.gte = new Date(query.startDate)
    }
    if (query.endDate) {
      range.lte = new Date(query.endDate)
    }
    return range
  }

  async findAll(token: any, query: GetDashboardStatsDto) {
    const userDetail: any = await this.userLoginRepository.getByParams({
      where: {
        access_token: token,
      },
      findOne: true,
      select: ["id", "user_id"],
      relations: ["user:id,team_member_id,role_id.role:id,name"],
    })

    const isDispatcher =
      userDetail?.user?.role?.name.toLowerCase() === "dispatcher"

    const totalClients = await this.getTotalClients(query)
    const totalHospitals = await this.getTotalHospitals(query)
    const totalCustomers = await this.getTotalCustomers(
      query,
      isDispatcher,
      userDetail?.user?.team_member_id,
    )
    const totalCustomerEscorts = await this.getTotalCustomerEscorts(query)
    const totalTrips = await this.getTotalTrips(
      query,
      isDispatcher,
      userDetail?.user?.team_member_id,
    )
    const totalVehicles = await this.getTotalVehicles(
      query,
      isDispatcher,
      userDetail?.user?.team_member_id,
    )

    return {
      total_clients: totalClients,
      total_hospitals: totalHospitals,
      total_customers: totalCustomers,
      total_customer_escorts: totalCustomerEscorts,
      total_trips: totalTrips,
      total_vehicles: totalVehicles,
    }
  }

  async getTotalClients(query: GetDashboardStatsDto): Promise<number> {
    const createdAtWhere = this.createdAtRangeFromQuery(query)

    const params: QueryParams<ClientsCompany> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
      },
      getCountOnly: true,
    }
    return (await this.clientCompanyRepository.getByParams(params)) as number
  }

  async getTotalHospitals(query: GetDashboardStatsDto): Promise<number> {
    const createdAtWhere = this.createdAtRangeFromQuery(query)

    const params: QueryParams<Hospital> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
      },
      getCountOnly: true,
    }
    return (await this.hospitalRepository.getByParams(params)) as number
  }

  async getTotalCustomers(
    query: GetDashboardStatsDto,
    isDispatcher: boolean,
    teamMemberId: number,
  ) {
    const createdAtWhere = this.createdAtRangeFromQuery(query)

    const totalParams: QueryParams<Customer> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
        ...(isDispatcher && { dispatcher_id: teamMemberId }),
      },
      whereNull: ["deleted_at"],
      getCountOnly: true,
    }

    const total = (await this.customerRepository.getByParams(
      totalParams,
    )) as number

    const fromClientsParams: QueryParams<Customer> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
      },
      whereNull: ["deleted_at"],
      whereNotNull: ["client_company_id"],
      getCountOnly: true,
    }

    const fromClients = (await this.customerRepository.getByParams(
      fromClientsParams,
    )) as number
    const direct = total - fromClients

    return {
      total,
      from_clients: fromClients,
      direct,
    }
  }

  async getTotalCustomerEscorts(query: GetDashboardStatsDto) {
    const createdAtWhere = this.createdAtRangeFromQuery(query)

    const totalParams: QueryParams<Escort> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
      },
      whereNull: ["deleted_at"],
      getCountOnly: true,
    }
    const total = (await this.escortRepository.getByParams(
      totalParams,
    )) as number

    const sponsoredParams: QueryParams<Escort> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
        is_sponsered: true,
      },
      getCountOnly: true,
    }
    const sponsored = (await this.escortRepository.getByParams(
      sponsoredParams,
    )) as number
    const nonSponsored = total - sponsored

    return {
      total,
      sponsored,
      non_sponsored: nonSponsored,
    }
  }

  async getTotalTrips(
    query: GetDashboardStatsDto,
    isDispatcher: boolean,
    teamMemberId: number,
  ) {
    const createdAtWhere = this.createdAtRangeFromQuery(query)

    const totalParams: QueryParams<Trip> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }), // TODO: condition on created_at or pickup_date
        ...(isDispatcher && { "entity_customer.dispatcher_id": teamMemberId }),
      },
      ...(isDispatcher && { relations: ["customer:dispatcher_id"] }),
      getCountOnly: true,
    }

    const total = (await this.tripRepository.getByParams(totalParams)) as number

    const completedParams: QueryParams<Trip> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }), //TODO: condition on created_at or complete_date
        status: "completed",
        ...(isDispatcher && { "entity_customer.dispatcher_id": teamMemberId }),
      },
      ...(isDispatcher && { relations: ["customer:dispatcher_id"] }),
      getCountOnly: true,
    }

    const completed = (await this.tripRepository.getByParams(
      completedParams,
    )) as number

    const cancelledParams: QueryParams<Trip> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }), // TODO: condition on created_at or cancel_date
        status: "cancelled",
        ...(isDispatcher && { "entity_customer.dispatcher_id": teamMemberId }),
      },
      ...(isDispatcher && { relations: ["customer:dispatcher_id"] }),
      getCountOnly: true,
    }

    const cancelled = (await this.tripRepository.getByParams(
      cancelledParams,
    )) as number

    const ongoingParams: QueryParams<Trip> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }), // TODO: condition on created_at or pickup_date
        status: "ongoing",
        ...(isDispatcher && { "entity_customer.dispatcher_id": teamMemberId }),
      },
      ...(isDispatcher && { relations: ["customer:dispatcher_id"] }),
      getCountOnly: true,
    }

    const ongoing = (await this.tripRepository.getByParams(
      ongoingParams,
    )) as number

    return {
      total,
      completed,
      cancelled,
      ongoing,
    }
  }

  async getTotalVehicles(
    query: GetDashboardStatsDto,
    isDispatcher: boolean,
    teamMemberId: number,
  ) {
    const createdAtWhere = this.createdAtRangeFromQuery(query)

    const totalParams: QueryParams<FleetManagement> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
        ...(isDispatcher && { assigned_dispatcher_id: teamMemberId }),
      },
      getCountOnly: true,
    }
    const total = (await this.fleetManagementRepository.getByParams(
      totalParams,
    )) as number

    const assignedParams: QueryParams<FleetManagement> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }), // TODO: condition on created_at or updated_at
        ...(isDispatcher && { assigned_dispatcher_id: teamMemberId }),
      },
      whereNotNull: ["assigned_driver"],

      getCountOnly: true,
    }
    const assigned = (await this.fleetManagementRepository.getByParams(
      assignedParams,
    )) as number
    const unassigned = total - assigned

    const nuskinParams: QueryParams<FleetManagement> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
        vehicle_ownership: "nuskin",
      },
      getCountOnly: true,
    }
    const nuskin = (await this.fleetManagementRepository.getByParams(
      nuskinParams,
    )) as number

    const driverParams: QueryParams<FleetManagement> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
        vehicle_ownership: "driver",
      },
      getCountOnly: true,
    }
    const driver = (await this.fleetManagementRepository.getByParams(
      driverParams,
    )) as number

    const thirdPartyParams: QueryParams<FleetManagement> = {
      where: {
        ...(createdAtWhere && { created_at: createdAtWhere }),
        vehicle_ownership: "third-party",
      },
      getCountOnly: true,
    }
    const thirdParty = (await this.fleetManagementRepository.getByParams(
      thirdPartyParams,
    )) as number

    return {
      total,
      assigned,
      unassigned,
      owner: {
        nuskin,
        driver,
        third_party: thirdParty,
      },
    }
  }
}
