import { CabinetEventModel } from '../../schemas/cabinet-event.schema.js';
import type {
  CabinetEventType,
  CabinetEventSourceType,
  CabinetEventQueryInput,
  SpendingSummaryQueryInput,
} from '@meshitrack/shared';

export interface CreateCabinetEventData {
  householdId: string;
  userId: string;
  cabinetItemId: string;
  medicineId: string;
  medicineName: string;
  eventType: CabinetEventType;
  quantity: number;
  quantityBefore: number;
  quantityAfter: number;
  unitPrice?: number;
  totalPrice?: number;
  currency?: string;
  storeId?: string;
  storeName?: string;
  sourceType: CabinetEventSourceType;
  sourceId?: string;
  reason?: string;
  notes?: string;
}

export class CabinetEventsRepository {
  public async create(data: CreateCabinetEventData) {
    const event = new CabinetEventModel(data);
    const saved = await event.save();
    return saved.toObject();
  }

  public async createMany(events: CreateCabinetEventData[]) {
    return CabinetEventModel.insertMany(events);
  }

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

    if (query.medicineId) filter['medicineId'] = query.medicineId;
    if (query.eventType) filter['eventType'] = query.eventType;
    if (query.startDate || query.endDate) {
      const dateFilter: Record<string, string> = {};
      if (query.startDate) dateFilter['$gte'] = query.startDate;
      if (query.endDate) dateFilter['$lte'] = query.endDate;
      filter['createdAt'] = dateFilter;
    }

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

    const limit = query.limit;
    const items = await CabinetEventModel.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 findByCabinetItem(
    householdId: string,
    cabinetItemId: string,
    query: { cursor?: string; limit: number },
  ) {
    const filter: Record<string, unknown> = { householdId, cabinetItemId };

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

    const limit = query.limit;
    const items = await CabinetEventModel.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 getSpendingSummary(householdId: string, query: SpendingSummaryQueryInput) {
    const match: Record<string, unknown> = {
      householdId,
      eventType: 'purchased',
      unitPrice: { $ne: null },
    };
    if (query.medicineId) match['medicineId'] = query.medicineId;
    if (query.startDate || query.endDate) {
      const dateFilter: Record<string, string> = {};
      if (query.startDate) dateFilter['$gte'] = query.startDate;
      if (query.endDate) dateFilter['$lte'] = query.endDate;
      match['createdAt'] = dateFilter;
    }

    const dateFormat =
      query.period === 'year' ? '%Y' : query.period === 'quarter' ? '%Y-Q%q' : '%Y-%m';

    const [byMedicine, byPeriod] = await Promise.all([
      CabinetEventModel.aggregate([
        { $match: match },
        {
          $group: {
            _id: '$medicineId',
            medicineName: { $first: '$medicineName' },
            totalSpent: { $sum: { $multiply: ['$unitPrice', '$quantity'] } },
            totalQuantity: { $sum: '$quantity' },
            purchaseCount: { $sum: 1 },
            currency: { $first: '$currency' },
          },
        },
        {
          $addFields: {
            avgUnitPrice: {
              $cond: [
                { $gt: ['$totalQuantity', 0] },
                { $divide: ['$totalSpent', '$totalQuantity'] },
                0,
              ],
            },
          },
        },
        { $sort: { totalSpent: -1 } },
      ]).exec(),
      CabinetEventModel.aggregate([
        { $match: match },
        {
          $group: {
            _id: { $dateToString: { format: dateFormat, date: '$createdAt' } },
            totalSpent: { $sum: { $multiply: ['$unitPrice', '$quantity'] } },
          },
        },
        { $sort: { _id: 1 } },
      ]).exec(),
    ]);

    const totalSpent = byMedicine.reduce(
      (sum: number, m: Record<string, unknown>) => sum + (m.totalSpent as number),
      0,
    );
    const currency =
      byMedicine.length > 0 ? ((byMedicine[0].currency as string | null) ?? null) : null;

    return {
      totalSpent,
      currency,
      byMedicine: byMedicine.map((m: Record<string, unknown>) => ({
        medicineId: m._id as string,
        medicineName: m.medicineName as string,
        totalSpent: m.totalSpent as number,
        totalQuantity: m.totalQuantity as number,
        avgUnitPrice: m.avgUnitPrice as number,
        purchaseCount: m.purchaseCount as number,
      })),
      byPeriod: byPeriod.map((p: Record<string, unknown>) => ({
        period: p._id as string,
        totalSpent: p.totalSpent as number,
      })),
    };
  }

  public async getAvgUnitPriceByMedicine(householdId: string, medicineIds: string[]) {
    if (medicineIds.length === 0)
      return new Map<string, { avgUnitPrice: number; currency: string | null }>();

    const results = await CabinetEventModel.aggregate([
      {
        $match: {
          householdId,
          eventType: 'purchased',
          medicineId: { $in: medicineIds },
          unitPrice: { $ne: null },
        },
      },
      {
        $group: {
          _id: '$medicineId',
          totalSpent: { $sum: { $multiply: ['$unitPrice', '$quantity'] } },
          totalQuantity: { $sum: '$quantity' },
          currency: { $first: '$currency' },
        },
      },
      {
        $addFields: {
          avgUnitPrice: {
            $cond: [
              { $gt: ['$totalQuantity', 0] },
              { $divide: ['$totalSpent', '$totalQuantity'] },
              0,
            ],
          },
        },
      },
    ]).exec();

    const map = new Map<string, { avgUnitPrice: number; currency: string | null }>();
    for (const r of results) {
      map.set(r._id as string, {
        avgUnitPrice: r.avgUnitPrice as number,
        currency: (r.currency as string | null) ?? null,
      });
    }
    return map;
  }
}
