import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { FilterQuery, Model, Types, UpdateQuery } from 'mongoose';
import { Instrument, InstrumentDocument } from './schemas/instrument.schema';
import { RepairShop, RepairShopDocument } from './schemas/repair-shop.schema';

@Injectable()
export class MarketplaceRepository {
  constructor(
    @InjectModel(Instrument.name)
    private readonly instrumentModel: Model<InstrumentDocument>,
    @InjectModel(RepairShop.name)
    private readonly repairShopModel: Model<RepairShopDocument>,
  ) {}

  async create(ownerAdminId: string, payload: Partial<Instrument>): Promise<InstrumentDocument> {
    return this.instrumentModel.create({
      ...payload,
      ownerAdminId: new Types.ObjectId(ownerAdminId),
    });
  }

  async findById(instrumentId: string): Promise<InstrumentDocument | null> {
    if (!Types.ObjectId.isValid(instrumentId)) {
      return null;
    }
    return this.instrumentModel
      .findById(instrumentId)
      .populate({ path: 'ownerAdminId', select: 'name username email avatar isDisabled shopName' })
      .exec();
  }

  async updateById(
    instrumentId: string,
    payload: UpdateQuery<InstrumentDocument>,
  ): Promise<InstrumentDocument | null> {
    if (!Types.ObjectId.isValid(instrumentId)) {
      return null;
    }

    return this.instrumentModel
      .findByIdAndUpdate(instrumentId, payload, { new: true })
      .populate({ path: 'ownerAdminId', select: 'name username email avatar isDisabled shopName' })
      .exec();
  }

  async deleteById(instrumentId: string): Promise<InstrumentDocument | null> {
    if (!Types.ObjectId.isValid(instrumentId)) {
      return null;
    }
    return this.instrumentModel.findByIdAndDelete(instrumentId).exec();
  }

  async findMany(
    filter: FilterQuery<InstrumentDocument>,
    skip: number,
    limit: number,
    sort: Record<string, 1 | -1> = { createdAt: -1 },
  ): Promise<InstrumentDocument[]> {
    return this.instrumentModel
      .find(filter)
      .populate({ path: 'ownerAdminId', select: 'name username email avatar isDisabled shopName' })
      .sort(sort)
      .skip(skip)
      .limit(limit)
      .exec();
  }

  async findManyPublic(
    filter: FilterQuery<InstrumentDocument>,
    skip: number,
    limit: number,
    sort: Record<string, 1 | -1> = { createdAt: -1 },
  ): Promise<Record<string, unknown>[]> {
    return this.instrumentModel
      .aggregate([
        { $match: filter },
        {
          $lookup: {
            from: 'users',
            localField: 'ownerAdminId',
            foreignField: '_id',
            as: 'ownerAdmin',
          },
        },
        { $unwind: '$ownerAdmin' },
        { $match: { 'ownerAdmin.isDisabled': false } },
        { $sort: sort },
        { $skip: skip },
        { $limit: limit },
        {
          $project: {
            _id: 1,
            title: 1,
            description: 1,
            price: 1,
            currency: 1,
            quantity: 1,
            imageUrls: 1,
            isActive: 1,
            condition: 1,
            instrumentType: 1,
            listingCategory: 1,
            createdAt: 1,
            updatedAt: 1,
            ownerAdminId: {
              _id: '$ownerAdmin._id',
              name: '$ownerAdmin.name',
              username: '$ownerAdmin.username',
              email: '$ownerAdmin.email',
              avatar: '$ownerAdmin.avatar',
              shopName: '$ownerAdmin.shopName',
            },
          },
        },
      ])
      .exec();
  }

  async countPublic(filter: FilterQuery<InstrumentDocument>): Promise<number> {
    const rows = await this.instrumentModel
      .aggregate([
        { $match: filter },
        {
          $lookup: {
            from: 'users',
            localField: 'ownerAdminId',
            foreignField: '_id',
            as: 'ownerAdmin',
          },
        },
        { $unwind: '$ownerAdmin' },
        { $match: { 'ownerAdmin.isDisabled': false } },
        { $count: 'count' },
      ])
      .exec();

    return rows[0]?.count ?? 0;
  }

  async count(filter: FilterQuery<InstrumentDocument>): Promise<number> {
    return this.instrumentModel.countDocuments(filter).exec();
  }

  async createRepairShop(
    ownerAdminId: string,
    payload: Partial<RepairShop>,
  ): Promise<RepairShopDocument> {
    return this.repairShopModel.create({
      ...payload,
      ownerAdminId: new Types.ObjectId(ownerAdminId),
    });
  }

  async findRepairShopByOwnerAdminId(ownerAdminId: string): Promise<RepairShopDocument | null> {
    if (!Types.ObjectId.isValid(ownerAdminId)) {
      return null;
    }

    return this.repairShopModel
      .findOne({ ownerAdminId: new Types.ObjectId(ownerAdminId) })
      .populate({ path: 'ownerAdminId', select: 'name username email avatar isDisabled shopName' })
      .exec();
  }

  async findRepairShopById(repairShopId: string): Promise<RepairShopDocument | null> {
    if (!Types.ObjectId.isValid(repairShopId)) {
      return null;
    }
    return this.repairShopModel
      .findById(repairShopId)
      .populate({ path: 'ownerAdminId', select: 'name username email avatar isDisabled shopName' })
      .exec();
  }

  async updateRepairShopById(
    repairShopId: string,
    payload: UpdateQuery<RepairShopDocument>,
  ): Promise<RepairShopDocument | null> {
    if (!Types.ObjectId.isValid(repairShopId)) {
      return null;
    }

    return this.repairShopModel
      .findByIdAndUpdate(repairShopId, payload, { new: true })
      .populate({ path: 'ownerAdminId', select: 'name username email avatar isDisabled shopName' })
      .exec();
  }

  async deleteRepairShopById(repairShopId: string): Promise<RepairShopDocument | null> {
    if (!Types.ObjectId.isValid(repairShopId)) {
      return null;
    }
    return this.repairShopModel.findByIdAndDelete(repairShopId).exec();
  }

  async findManyRepairShops(
    filter: FilterQuery<RepairShopDocument>,
    skip: number,
    limit: number,
    sort: Record<string, 1 | -1> = { createdAt: -1 },
  ): Promise<RepairShopDocument[]> {
    return this.repairShopModel
      .find(filter)
      .populate({ path: 'ownerAdminId', select: 'name username email avatar isDisabled shopName' })
      .sort(sort)
      .skip(skip)
      .limit(limit)
      .exec();
  }

  async findManyRepairShopsPublic(
    filter: FilterQuery<RepairShopDocument>,
    skip: number,
    limit: number,
    sort: Record<string, 1 | -1> = { createdAt: -1 },
  ): Promise<Record<string, unknown>[]> {
    return this.repairShopModel
      .aggregate([
        { $match: filter },
        {
          $lookup: {
            from: 'users',
            localField: 'ownerAdminId',
            foreignField: '_id',
            as: 'ownerAdmin',
          },
        },
        { $unwind: '$ownerAdmin' },
        { $match: { 'ownerAdmin.isDisabled': false } },
        { $sort: sort },
        { $skip: skip },
        { $limit: limit },
        {
          $project: {
            _id: 1,
            name: 1,
            description: 1,
            services: 1,
            phone: 1,
            whatsapp: 1,
            imageUrls: 1,
            location: 1,
            latitude: 1,
            longitude: 1,
            isActive: 1,
            createdAt: 1,
            updatedAt: 1,
            ownerAdminId: {
              _id: '$ownerAdmin._id',
              name: '$ownerAdmin.name',
              username: '$ownerAdmin.username',
              email: '$ownerAdmin.email',
              avatar: '$ownerAdmin.avatar',
              shopName: '$ownerAdmin.shopName',
            },
          },
        },
      ])
      .exec();
  }

  async countRepairShopsPublic(filter: FilterQuery<RepairShopDocument>): Promise<number> {
    const rows = await this.repairShopModel
      .aggregate([
        { $match: filter },
        {
          $lookup: {
            from: 'users',
            localField: 'ownerAdminId',
            foreignField: '_id',
            as: 'ownerAdmin',
          },
        },
        { $unwind: '$ownerAdmin' },
        { $match: { 'ownerAdmin.isDisabled': false } },
        { $count: 'count' },
      ])
      .exec();

    return rows[0]?.count ?? 0;
  }

  async countRepairShops(filter: FilterQuery<RepairShopDocument>): Promise<number> {
    return this.repairShopModel.countDocuments(filter).exec();
  }
}
