import { PurchaseModel } from '../../schemas/purchase.schema.js';
import type { PurchaseQueryInput, UpdatePurchaseInput } from '@meshitrack/shared';

export interface CreatePurchaseItemData {
  medicineProductId?: string;
  medicineId?: string;
  foodProductId?: string;
  name: string;
  quantity: number;
  unit: string;
  actualPrice?: number;
  currency?: string;
  priceRecordId?: string;
  addedToCabinet?: boolean;
}

export interface CreatePurchaseData {
  householdId: string;
  storeId: string;
  storeName: string;
  status: string;
  items: CreatePurchaseItemData[];
  notes?: string;
  purchasedAt: Date;
  createdBy: string;
}

export class PurchasesRepository {
  public async create(data: CreatePurchaseData) {
    const purchase = new PurchaseModel(data);
    const saved = await purchase.save();
    return saved.toObject();
  }

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

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

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

    const limit = query.limit;
    const items = await PurchaseModel.find(filter)
      .sort({ _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 PurchaseModel.findOne({ _id: id, householdId, isDeleted: false }).lean().exec();
  }

  public async update(id: string, householdId: string, data: UpdatePurchaseInput) {
    const updateSet: Record<string, unknown> = {};
    if (data.notes !== undefined) updateSet['notes'] = data.notes;
    if (data.items !== undefined) updateSet['items'] = data.items;

    return PurchaseModel.findOneAndUpdate(
      { _id: id, householdId, isDeleted: false },
      { $set: updateSet },
      { new: true, lean: true },
    ).exec();
  }

  public async markItemsAddedToCabinet(
    purchaseId: string,
    householdId: string,
    itemIndices: number[],
  ) {
    // Build update using positional array filters
    const updateSet: Record<string, unknown> = {};
    for (const idx of itemIndices) {
      updateSet[`items.${idx}.addedToCabinet`] = true;
    }

    return PurchaseModel.findOneAndUpdate(
      { _id: purchaseId, householdId, isDeleted: false },
      { $set: updateSet },
      { new: true, lean: true },
    ).exec();
  }

  public async receiveAll(purchaseId: string, householdId: string) {
    return PurchaseModel.findOneAndUpdate(
      { _id: purchaseId, householdId, isDeleted: false },
      {
        $set: {
          status: 'in_cabinet',
          receivedAt: new Date(),
          'items.$[].addedToCabinet': true,
        },
      },
      { new: true, lean: true },
    ).exec();
  }

  public async getPendingMedicineStock(
    householdId: string,
  ): Promise<{ medicineId: string; totalUnits: number }[]> {
    const results = await PurchaseModel.aggregate([
      { $match: { householdId, status: 'ordered', isDeleted: false } },
      { $unwind: '$items' },
      {
        $match: {
          'items.medicineId': { $exists: true, $ne: null },
          'items.addedToCabinet': false,
        },
      },
      {
        $group: {
          _id: '$items.medicineId',
          totalUnits: { $sum: '$items.quantity' },
        },
      },
      { $project: { _id: 0, medicineId: '$_id', totalUnits: 1 } },
    ]).exec();

    return results as { medicineId: string; totalUnits: number }[];
  }

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