import { PantryItemModel } from '../../schemas/pantry-item.schema.js';
import type { ItemStatus } from '@meshitrack/shared';

interface FindByHouseholdQuery {
  storageLocation?: string;
  status?: string;
  urgency?: string;
  productId?: string;
  cursor?: string;
  limit: number;
}

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

    if (query.storageLocation) filter['storageLocation'] = query.storageLocation;

    if (query.status) {
      const statuses = query.status.split(',').filter(Boolean);
      filter['status'] = statuses.length === 1 ? statuses[0] : { $in: statuses };
    }

    if (query.urgency) {
      const urgencies = query.urgency.split(',').filter(Boolean);
      filter['freshnessEstimate.urgency'] =
        urgencies.length === 1 ? urgencies[0] : { $in: urgencies };
    }

    if (query.productId) filter['productId'] = query.productId;

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

    const limit = query.limit;
    const items = await PantryItemModel.find(filter)
      .sort({ 'freshnessEstimate.daysRemaining': 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 PantryItemModel.findOne({ _id: id, householdId }).lean().exec();
  }

  public async findExpiringSoon(householdId: string, days: number, cursor?: string, limit = 20) {
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() + days);

    const filter: Record<string, unknown> = {
      householdId,
      status: { $in: ['sealed', 'opened', 'prepared'] },
      'freshnessEstimate.estimatedExpiryDate': { $lte: cutoff },
      'freshnessEstimate.daysRemaining': { $gte: -3 },
    };

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

    const items = await PantryItemModel.find(filter)
      .sort({ 'freshnessEstimate.daysRemaining': 1, _id: 1 })
      .limit(limit + 1)
      .lean()
      .exec();

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

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

  public async findActiveByHousehold(householdId: string) {
    return PantryItemModel.find({
      householdId,
      status: { $in: ['sealed', 'opened', 'prepared'] },
    })
      .sort({ 'freshnessEstimate.daysRemaining': 1, _id: 1 })
      .lean()
      .exec();
  }

  public async create(data: Record<string, unknown>) {
    const doc = new PantryItemModel(data);
    const saved = await doc.save();
    return saved.toObject();
  }

  public async update(id: string, householdId: string, data: Record<string, unknown>) {
    return PantryItemModel.findOneAndUpdate({ _id: id, householdId }, { $set: data }, { new: true })
      .lean()
      .exec();
  }

  public async updateFreshness(
    id: string,
    freshnessEstimate: Record<string, unknown>,
    status?: string,
  ) {
    const update: Record<string, unknown> = { freshnessEstimate };
    if (status) update['status'] = status;
    return PantryItemModel.findByIdAndUpdate(id, { $set: update }).exec();
  }

  public async delete(id: string, householdId: string) {
    return PantryItemModel.findOneAndDelete({ _id: id, householdId }).exec();
  }

  public async getWasteStats(householdId: string, start: Date, end: Date) {
    return PantryItemModel.aggregate([
      {
        $match: {
          householdId,
          status: { $in: ['consumed', 'discarded'] },
          updatedAt: { $gte: start, $lte: end },
        },
      },
      {
        $group: {
          _id: null,
          totalConsumed: {
            $sum: { $cond: [{ $eq: ['$status', 'consumed'] }, 1, 0] },
          },
          totalDiscarded: {
            $sum: { $cond: [{ $eq: ['$status', 'discarded'] }, 1, 0] },
          },
        },
      },
    ]).exec();
  }

  public async getTopWastedProducts(householdId: string, start: Date, end: Date, limit = 5) {
    return PantryItemModel.aggregate([
      {
        $match: {
          householdId,
          status: 'discarded',
          updatedAt: { $gte: start, $lte: end },
        },
      },
      {
        $group: {
          _id: '$productId',
          productName: { $first: '$productName' },
          count: { $sum: 1 },
        },
      },
      { $sort: { count: -1 } },
      { $limit: limit },
      {
        $project: {
          productId: '$_id',
          productName: 1,
          count: 1,
          _id: 0,
        },
      },
    ]).exec();
  }

  public async findByIds(ids: string[], householdId: string) {
    return PantryItemModel.find({
      _id: { $in: ids },
      householdId,
    })
      .lean()
      .exec();
  }

  public async bulkUpdateStatus(
    ids: string[],
    householdId: string,
    status: ItemStatus,
    extra: Record<string, unknown> = {},
  ) {
    const result = await PantryItemModel.updateMany(
      { _id: { $in: ids }, householdId },
      { $set: { status, ...extra } },
    ).exec();
    return result.modifiedCount;
  }
}
