import { CabinetItemModel } from '../../schemas/cabinet-item.schema.js';
import type {
  CabinetItemStatus,
  CreateCabinetItemInput,
  UpdateCabinetItemInput,
} from '@meshitrack/shared';

interface FindByHouseholdQuery {
  medicineId?: string;
  status?: CabinetItemStatus;
  expiringWithin?: number;
  cursor?: string;
  limit: number;
}

export class CabinetRepository {
  public async findByHousehold(householdId: string, query: FindByHouseholdQuery) {
    const filter: Record<string, unknown> = { householdId, isDeleted: false };

    if (query.medicineId) filter['medicineId'] = query.medicineId;
    if (query.status) filter['status'] = query.status;

    if (query.expiringWithin) {
      const cutoff = new Date();
      cutoff.setDate(cutoff.getDate() + query.expiringWithin);
      filter['expirationDate'] = { $lte: cutoff, $gt: new Date() };
      filter['status'] = 'active';
    }

    if (query.cursor) {
      const id = Buffer.from(query.cursor, 'base64').toString();
      filter['_id'] = { $gt: id };
    }

    const limit = query.limit;
    const items = await CabinetItemModel.find(filter)
      .sort({ medicineName: 1, medicineForm: 1, medicineStrength: 1, _id: 1 })
      .limit(limit + 1)
      .lean()
      .exec();

    const hasMore = items.length > limit;
    const data = hasMore ? items.slice(0, limit) : items;
    const cursor =
      data.length > 0 ? Buffer.from(data[data.length - 1]._id.toString()).toString('base64') : null;

    return { data, pagination: { cursor: hasMore ? cursor : null, hasMore } };
  }

  public async findById(id: string, householdId: string) {
    return CabinetItemModel.findOne({ _id: id, householdId, isDeleted: false }).lean().exec();
  }

  public async getAggregateSummary(householdId: string) {
    return CabinetItemModel.aggregate([
      { $match: { householdId, isDeleted: false, status: 'active' } },
      {
        $group: {
          _id: '$medicineId',
          medicineName: { $first: '$medicineName' },
          medicineStrength: { $first: '$medicineStrength' },
          medicineStrengthUnit: { $first: '$medicineStrengthUnit' },
          medicineForm: { $first: '$medicineForm' },
          totalQuantity: { $sum: '$quantity' },
          unit: { $first: '$unit' },
          earliestExpiry: { $min: '$expirationDate' },
          itemCount: { $sum: 1 },
        },
      },
      { $sort: { medicineName: 1, medicineForm: 1, medicineStrength: 1, _id: 1 } },
    ]).exec();
  }

  public async create(
    data: CreateCabinetItemInput & {
      medicineName: string;
      medicineStrength: number;
      medicineStrengthUnit: string;
      medicineForm: string;
      medicineProductBrand?: string;
      concentration?: number;
      concentrationUnit?: string;
    },
    householdId: string,
    createdBy: string,
  ) {
    const item = new CabinetItemModel({ ...data, householdId, createdBy });
    const saved = await item.save();
    return saved.toObject();
  }

  public async update(id: string, householdId: string, data: UpdateCabinetItemInput) {
    return CabinetItemModel.findOneAndUpdate(
      { _id: id, householdId, isDeleted: false },
      { $set: data },
      { new: true, lean: true },
    ).exec();
  }

  public async adjustQuantity(id: string, householdId: string, delta: number) {
    const item = await CabinetItemModel.findOne({
      _id: id,
      householdId,
      isDeleted: false,
    })
      .lean()
      .exec();

    if (!item) return null;

    const newQuantity = Math.max(0, item.quantity + delta);
    const newStatus =
      newQuantity === 0 ? 'depleted' : item.status === 'depleted' ? 'active' : item.status;

    return CabinetItemModel.findOneAndUpdate(
      { _id: id, householdId, isDeleted: false },
      { $set: { quantity: newQuantity, status: newStatus } },
      { new: true, lean: true },
    ).exec();
  }

  public async findExpiringSoon(householdId: string, withinDays: number) {
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() + withinDays);

    return CabinetItemModel.find({
      householdId,
      isDeleted: false,
      status: 'active',
      expirationDate: { $lte: cutoff, $gt: new Date() },
    })
      .sort({ expirationDate: 1 })
      .lean()
      .exec();
  }

  public async countByMedicineId(medicineId: string): Promise<number> {
    return CabinetItemModel.countDocuments({ medicineId, isDeleted: false }).exec();
  }

  public async softDelete(id: string, householdId: string) {
    return CabinetItemModel.findOneAndUpdate(
      { _id: id, householdId, isDeleted: false },
      { $set: { isDeleted: true } },
      { new: true, lean: true },
    ).exec();
  }

  public async discard(id: string, householdId: string) {
    return CabinetItemModel.findOneAndUpdate(
      { _id: id, householdId, isDeleted: false },
      { $set: { quantity: 0, status: 'depleted', isDeleted: true } },
      { new: true, lean: true },
    ).exec();
  }

  public async findActiveByMedicineForFEFO(householdId: string, medicineId: string) {
    return CabinetItemModel.find({
      householdId,
      medicineId,
      isDeleted: false,
      status: 'active',
      quantity: { $gt: 0 },
    })
      .sort({ expirationDate: 1, _id: 1 })
      .lean()
      .exec();
  }
}
