import { Injectable } from "@nestjs/common"
import { Repository } from "typeorm"
import { BaseAbstractRepository } from "src/common/repository/base.repository"
import { Rating } from "../entities/rating.entity"
import { InjectRepository } from "@nestjs/typeorm"
import { RATING_TYPES } from "../constants/rating-type.constants"

@Injectable()
export class RatingRepository extends BaseAbstractRepository<Rating> {
  constructor(
    @InjectRepository(Rating)
    private readonly ratingRepository: Repository<Rating>,
  ) {
    super(ratingRepository)
  }

  async getAverageRating(
    params: Partial<Record<keyof Rating, any>>,
  ): Promise<number | null> {
    const qb = this.ratingRepository.createQueryBuilder("rating")

    // Dynamically add where conditions from params
    Object.entries(params).forEach(([key, value]) => {
      qb.andWhere(`rating.${key} = :${key}`, { [key]: value })
    })

    const { avg } = await qb
      .select("AVG(rating.rating)", "avg")
      .getRawOne<{ avg: string }>()

    return avg ? parseFloat(avg) : null
  }

  async getRatingForTripId(tripId: number) {
    const qb = this.ratingRepository
      .createQueryBuilder("rating")
      .where("rating.trip_id = :tripId", { tripId })

    // Conditionally join based on rating_type
    qb.leftJoinAndSelect(
      "rating.rater_driver",
      "raterDriver",
      `rating.rating_type IN ('${RATING_TYPES.DRIVER_TO_CUSTOMER}', '${RATING_TYPES.DRIVER_TO_DISPATCHER}', '${RATING_TYPES.DISPATCHER_TO_DRIVER}', '${RATING_TYPES.DISPATCHER_TO_CUSTOMER}')`,
    )
    qb.leftJoinAndSelect(
      "rating.rated_customer",
      "ratedCustomer",
      `rating.rating_type IN ('${RATING_TYPES.DRIVER_TO_CUSTOMER}', '${RATING_TYPES.DISPATCHER_TO_CUSTOMER}')`,
    )
    qb.leftJoinAndSelect(
      "rating.rater_customer",
      "raterCustomer",
      `rating.rating_type IN ('${RATING_TYPES.CUSTOMER_TO_DRIVER}', '${RATING_TYPES.CUSTOMER_TO_DISPATCHER}')`,
    )
    qb.leftJoinAndSelect(
      "rating.rated_driver",
      "ratedDriver",
      `rating.rating_type IN ('${RATING_TYPES.CUSTOMER_TO_DRIVER}', '${RATING_TYPES.DISPATCHER_TO_DRIVER}', '${RATING_TYPES.CUSTOMER_TO_DISPATCHER}', '${RATING_TYPES.DRIVER_TO_DISPATCHER}')`,
    )
      .addSelect([
        "raterDriver.id",
        "raterDriver.first_name",
        "raterDriver.last_name",
      ])
      .addSelect(["ratedCustomer.id", "ratedCustomer.customer_name"])
      .addSelect(["raterCustomer.id", "raterCustomer.customer_name"])
      .addSelect([
        "ratedDriver.id",
        "ratedDriver.first_name",
        "ratedDriver.last_name",
      ])
      .addSelect(["rating.rating", "rating.tags", "rating.other_input"])

    const ratings = await qb.getMany()

    const result = {
      customer: { rating: 0, tags: [], other_input: "", count: 0 },
      driver: { rating: 0, tags: [], other_input: "", count: 0 },
      dispatcher: { rating: 0, tags: [], other_input: "", count: 0 },
    }

    const customerRatings = ratings.filter(
      (r) =>
        r.rating_type === RATING_TYPES.DRIVER_TO_CUSTOMER ||
        r.rating_type === RATING_TYPES.DISPATCHER_TO_CUSTOMER,
    )
    const driverRatings = ratings.filter(
      (r) =>
        r.rating_type === RATING_TYPES.CUSTOMER_TO_DRIVER ||
        r.rating_type === RATING_TYPES.DISPATCHER_TO_DRIVER,
    )
    const dispatcherRatings = ratings.filter(
      (r) =>
        r.rating_type === RATING_TYPES.CUSTOMER_TO_DISPATCHER ||
        r.rating_type === RATING_TYPES.DRIVER_TO_DISPATCHER,
    )

    if (customerRatings.length > 0) {
      const totalRating = customerRatings.reduce((sum, r) => sum + r.rating, 0)
      result.customer.rating = parseFloat(
        (totalRating / customerRatings.length).toFixed(1),
      )
      result.customer.tags = [
        ...new Set(customerRatings.flatMap((r) => r.tags || [])),
      ]
      result.customer.other_input =
        customerRatings.find((r) => r.other_input)?.other_input || ""
      result.customer.count = customerRatings.length
    }

    if (driverRatings.length > 0) {
      const totalRating = driverRatings.reduce((sum, r) => sum + r.rating, 0)
      result.driver.rating = parseFloat(
        (totalRating / driverRatings.length).toFixed(1),
      )
      result.driver.tags = [
        ...new Set(driverRatings.flatMap((r) => r.tags || [])),
      ]
      result.driver.other_input =
        driverRatings.find((r) => r.other_input)?.other_input || ""
      result.driver.count = driverRatings.length
    }

    if (dispatcherRatings.length > 0) {
      const totalRating = dispatcherRatings.reduce(
        (sum, r) => sum + r.rating,
        0,
      )
      result.dispatcher.rating = parseFloat(
        (totalRating / dispatcherRatings.length).toFixed(1),
      )
      result.dispatcher.tags = [
        ...new Set(dispatcherRatings.flatMap((r) => r.tags || [])),
      ]
      result.dispatcher.other_input =
        dispatcherRatings.find((r) => r.other_input)?.other_input || ""
      result.dispatcher.count = dispatcherRatings.length
    }

    return result
  }
}
