import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { FilterQuery, Model, Types } from 'mongoose';
import { SupportTicket, SupportTicketDocument, SupportTicketStatus } from './schemas/support-ticket.schema';
import {
  SupportMessageSenderRole,
  SupportTicketMessage,
  SupportTicketMessageDocument,
} from './schemas/support-ticket-message.schema';

@Injectable()
export class SupportRepository {
  constructor(
    @InjectModel(SupportTicket.name)
    private readonly ticketModel: Model<SupportTicketDocument>,
    @InjectModel(SupportTicketMessage.name)
    private readonly messageModel: Model<SupportTicketMessageDocument>,
  ) {}

  async createTicket(payload: {
    userId: string;
    subject: string;
    category: string;
    priority: string;
    lastMessageAt: Date;
  }) {
    return this.ticketModel.create({
      userId: new Types.ObjectId(payload.userId),
      subject: payload.subject,
      category: payload.category,
      priority: payload.priority,
      status: 'open',
      lastMessageAt: payload.lastMessageAt,
      resolvedAt: null,
      closedAt: null,
    });
  }

  async createMessage(payload: {
    ticketId: string;
    senderId?: string | null;
    senderIdentifier: string;
    senderRole: SupportMessageSenderRole;
    message: string;
    attachmentUrl?: string;
    attachmentType?: 'image' | 'file' | null;
    readByUser: boolean;
    readByAdmin: boolean;
  }) {
    return this.messageModel.create({
      ticketId: new Types.ObjectId(payload.ticketId),
      senderId: payload.senderId && Types.ObjectId.isValid(payload.senderId) ? new Types.ObjectId(payload.senderId) : null,
      senderIdentifier: payload.senderIdentifier,
      senderRole: payload.senderRole,
      message: payload.message,
      attachmentUrl: payload.attachmentUrl ?? '',
      attachmentType: payload.attachmentType ?? null,
      readByUser: payload.readByUser,
      readByAdmin: payload.readByAdmin,
    });
  }

  async findTicketById(ticketId: string) {
    return this.ticketModel.findById(ticketId).exec();
  }

  async findUserTicket(ticketId: string, userId: string) {
    return this.ticketModel
      .findOne({ _id: new Types.ObjectId(ticketId), userId: new Types.ObjectId(userId) })
      .exec();
  }

  async findUserTickets(filter: FilterQuery<SupportTicketDocument>, skip: number, limit: number, sort: Record<string, 1 | -1>) {
    return this.ticketModel.find(filter).sort(sort).skip(skip).limit(limit).exec();
  }

  async findAdminTickets(filter: FilterQuery<SupportTicketDocument>, skip: number, limit: number, sort: Record<string, 1 | -1>) {
    return this.ticketModel
      .find(filter)
      .populate({ path: 'userId', select: 'name username stageName avatar email isDisabled isVerified' })
      .populate({ path: 'assignedAdminId', select: 'name username stageName avatar email isDisabled isVerified' })
      .sort(sort)
      .skip(skip)
      .limit(limit)
      .exec();
  }

  async findAdminTicket(ticketId: string) {
    return this.ticketModel
      .findById(ticketId)
      .populate({ path: 'userId', select: 'name username stageName avatar email isDisabled isVerified' })
      .populate({ path: 'assignedAdminId', select: 'name username stageName avatar email isDisabled isVerified' })
      .exec();
  }

  async countTickets(filter: FilterQuery<SupportTicketDocument>) {
    return this.ticketModel.countDocuments(filter).exec();
  }

  async findMessages(ticketId: string) {
    return this.messageModel
      .find({ ticketId: new Types.ObjectId(ticketId) })
      .populate({ path: 'senderId', select: 'name username stageName avatar email isDisabled isVerified' })
      .sort({ createdAt: 1 })
      .exec();
  }

  async updateTicketAfterMessage(ticketId: string, updates: Partial<{ status: SupportTicketStatus }>) {
    const now = new Date();
    return this.ticketModel
      .findByIdAndUpdate(
        ticketId,
        {
          $set: {
            lastMessageAt: now,
            ...updates,
          },
        },
        { new: true },
      )
      .exec();
  }

  async closeTicket(ticketId: string, userId: string) {
    return this.ticketModel
      .findOneAndUpdate(
        { _id: new Types.ObjectId(ticketId), userId: new Types.ObjectId(userId) },
        { $set: { status: 'closed', closedAt: new Date() } },
        { new: true },
      )
      .exec();
  }

  async updateStatus(ticketId: string, status: SupportTicketStatus) {
    const update: Record<string, unknown> = { status };
    if (status === 'resolved') {
      update.resolvedAt = new Date();
    }
    if (status === 'closed') {
      update.closedAt = new Date();
    }
    return this.ticketModel.findByIdAndUpdate(ticketId, { $set: update }, { new: true }).exec();
  }

  async assign(ticketId: string, adminId?: string | null) {
    return this.ticketModel
      .findByIdAndUpdate(
        ticketId,
        { $set: { assignedAdminId: adminId && Types.ObjectId.isValid(adminId) ? new Types.ObjectId(adminId) : null } },
        { new: true },
      )
      .populate({ path: 'userId', select: 'name username stageName avatar email isDisabled isVerified' })
      .populate({ path: 'assignedAdminId', select: 'name username stageName avatar email isDisabled isVerified' })
      .exec();
  }
}
