import { ProductModel } from '../../schemas/product.schema.js';
import type { ProductQueryInput } from '@meshitrack/shared';

export class ProductsRepository {
  public async findByHousehold(householdId: string, query: ProductQueryInput) {
    const filter: Record<string, unknown> = { householdId, deletedAt: { $exists: false } };

    if (query.category) filter['category'] = query.category;
    if (query.barcode) filter['barcode'] = query.barcode;
    if (query.q) filter['name'] = { $regex: query.q, $options: 'i' };
    if (query.tags) {
      const tagList = query.tags.split(',').filter(Boolean);
      if (tagList.length > 0) filter['tags'] = { $all: tagList };
    }

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

    const limit = query.limit;
    const items = await ProductModel.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) {
    // Returns the product regardless of soft-delete status so historical references
    // in recipes, pantry, and grocery remain resolvable.
    return ProductModel.findOne({ _id: id, householdId }).lean().exec();
  }

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

  public async findByBarcode(householdId: string, barcode: string) {
    return ProductModel.findOne({ householdId, barcode, deletedAt: { $exists: false } })
      .lean()
      .exec();
  }

  public async findDuplicate(
    householdId: string,
    name: string,
    brand?: string,
    excludeId?: string,
  ) {
    const filter: Record<string, unknown> = {
      householdId,
      name,
      deletedAt: { $exists: false },
    };
    if (brand !== undefined) filter['brand'] = brand;
    if (excludeId) filter['_id'] = { $ne: excludeId };
    return ProductModel.findOne(filter).lean().exec();
  }

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

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

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

  public async bulkCreate(householdId: string, items: Record<string, unknown>[]) {
    const docs = items.map((item) => ({ ...item, householdId }));
    return ProductModel.insertMany(docs, { ordered: false });
  }
}
