import { Injectable } from "@nestjs/common"
import { Repository } from "typeorm"
import { InjectRepository } from "@nestjs/typeorm"
import { ChatRoom, ChatType } from "../entities/chat-room.entity"

@Injectable()
export class ChatRoomRepository {
  constructor(
    @InjectRepository(ChatRoom)
    private readonly chatRoomEntity: Repository<ChatRoom>,
  ) {}

  async save(roomData: Partial<ChatRoom>): Promise<ChatRoom> {
    const room = this.chatRoomEntity.create(roomData)
    return await this.chatRoomEntity.save(room)
  }

  async findByParams(options: {
    where?: any
    relations?: string[]
    select?: string[]
    orderBy?: any
    take?: number
    skip?: number
    findOne?: boolean
  }): Promise<ChatRoom | ChatRoom[]> {
    const query = this.chatRoomEntity.createQueryBuilder("room")

    if (options.where) {
      Object.entries(options.where).forEach(([key, value]) => {
        if (Array.isArray(value)) {
          query.andWhere(`room.${key} IN (:...${key})`, { [key]: value })
        } else {
          query.andWhere(`room.${key} = :${key}`, { [key]: value })
        }
      })
    }

    if (options.relations && options.relations.length > 0) {
      options.relations.forEach((relation) => {
        query.leftJoinAndSelect(`room.${relation}`, relation)
      })
    }

    if (options.select && options.select.length > 0) {
      query.select(options.select.map((field) => `room.${field}`))
    }

    if (options.orderBy) {
      Object.entries(options.orderBy).forEach(([field, direction]) => {
        query.orderBy(`room.${field}`, direction as "ASC" | "DESC")
      })
    }

    if (options.take) {
      query.take(options.take)
    }

    if (options.skip) {
      query.skip(options.skip)
    }

    if (options.findOne) {
      return await query.getOne()
    }

    return await query.getMany()
  }

  async findOrCreateChatRoom(
    chatType: ChatType,
    tripId?: number,
    participantIds?: { teamMemberId?: number; customerId?: number },
  ): Promise<ChatRoom> {
    // For trip-specific chats (driver-customer, customer-dispatcher)
    if (tripId) {
      const existingRoom = await this.chatRoomEntity.findOne({
        where: { chat_type: chatType, trip_id: tripId },
      })

      if (existingRoom) {
        return existingRoom
      }
    }

    // For driver-dispatcher chats, find existing room between the two specific team members
    if (
      chatType === ChatType.DRIVER_DISPATCHER &&
      participantIds?.teamMemberId
    ) {
      // Find existing room that contains BOTH specific team members
      // We need to get both participant IDs from the context
      const existingRoom = await this.chatRoomEntity
        .createQueryBuilder("room")
        .leftJoin("room.participants", "participant")
        .where("room.chat_type = :chatType", { chatType })
        .andWhere("room.trip_id IS NULL")
        .andWhere("participant.participant_type = 'team_member'")
        .andWhere("participant.participant_id = :teamMemberId", {
          teamMemberId: participantIds.teamMemberId,
        })
        .groupBy("room.id")
        .having("COUNT(DISTINCT participant.participant_id) >= 2")
        .getOne()

      if (existingRoom) {
        return existingRoom
      }
    }

    // For customer-dispatcher chats, find existing room between customer and dispatcher
    if (
      chatType === ChatType.CUSTOMER_DISPATCHER &&
      participantIds?.teamMemberId &&
      participantIds?.customerId
    ) {
      const existingRoom = await this.chatRoomEntity
        .createQueryBuilder("room")
        .leftJoin("room.participants", "participant")
        .where("room.chat_type = :chatType", { chatType })
        .andWhere("room.trip_id IS NULL")
        .andWhere(
          "(participant.participant_type = 'team_member' OR participant.participant_type = 'customer')",
        )
        .andWhere(
          "(participant.participant_id = :teamMemberId OR participant.participant_id = :customerId)",
          {
            teamMemberId: participantIds.teamMemberId,
            customerId: participantIds.customerId,
          },
        )
        .groupBy("room.id")
        .having("COUNT(DISTINCT participant.participant_id) >= 2")
        .getOne()

      if (existingRoom) {
        return existingRoom
      }
    }

    // Create new room
    const newRoom = await this.save({
      chat_type: chatType,
      trip_id: tripId,
    })

    return newRoom
  }

  async getChatRoomsByParticipant(
    participantType: string,
    participantId: number,
  ): Promise<ChatRoom[]> {
    return await this.chatRoomEntity
      .createQueryBuilder("room")
      .leftJoin("room.participants", "participant")
      .where("participant.participant_type = :participantType", {
        participantType,
      })
      .andWhere("participant.participant_id = :participantId", {
        participantId,
      })
      .leftJoinAndSelect("room.participants", "allParticipants")
      .leftJoinAndSelect("room.messages", "messages")
      .orderBy("room.created_at", "DESC")
      .getMany()
  }

  async findChatRoomBetweenParticipants(
    participant1Type: string,
    participant1Id: number,
    participant2Type: string,
    participant2Id: number,
    tripId?: number,
  ): Promise<ChatRoom | null> {
    const roomQuery = this.chatRoomEntity
      .createQueryBuilder("room")
      .leftJoin("room.participants", "participant")
      .where(
        "(participant.participant_type = :participant1Type AND participant.participant_id = :participant1Id) OR " +
          "(participant.participant_type = :participant2Type AND participant.participant_id = :participant2Id)",
        {
          participant1Type,
          participant1Id,
          participant2Type,
          participant2Id,
        },
      )
      .groupBy("room.id")
      .having("COUNT(DISTINCT participant.id) >= 2")

    if (tripId) {
      roomQuery.andWhere("room.trip_id = :tripId", { tripId })
    } else {
      roomQuery.andWhere("room.trip_id IS NULL")
    }

    const room = await roomQuery.getOne()

    // If room found, load it with participants and messages
    if (room) {
      return await this.chatRoomEntity.findOne({
        where: { id: room.id },
        relations: ["participants", "messages"],
      })
    }

    return null
  }
}
