import { MedicinePriceModel } from '../../schemas/medicine-price.schema.js';
import type {
  MedicinePriceHistoryQueryInput,
  MedicinePriceAnalyticsQueryInput,
} from '@meshitrack/shared';

export interface CreateMedicinePriceData {
  householdId: string;
  medicineProductId: string;
  medicineProductBrand: string;
  medicineId: string;
  medicineName: string;
  storeId: string;
  storeName: string;
  price: number;
  currency: string;
  quantity: number;
  unit: string;
  pricePerUnit: number;
  date: Date;
  isInsurancePrice: boolean;
  notes?: string;
  createdBy: string;
}

export class MedicinePricesRepository {
  public async create(data: CreateMedicinePriceData) {
    const record = new MedicinePriceModel(data);
    const saved = await record.save();
    return saved.toObject();
  }

  public async findByMedicine(
    householdId: string,
    medicineId: string,
    query: MedicinePriceHistoryQueryInput,
  ) {
    const filter: Record<string, unknown> = { householdId, medicineId };

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

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

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

    const limit = query.limit;
    const items = await MedicinePriceModel.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 compareStores(householdId: string, medicineId: string) {
    // Get the most recent price per store for this medicine
    const results = await MedicinePriceModel.aggregate([
      { $match: { householdId, medicineId } },
      { $sort: { storeId: 1, date: -1 } },
      {
        $group: {
          _id: '$storeId',
          storeName: { $first: '$storeName' },
          latestPrice: { $first: '$price' },
          latestPricePerUnit: { $first: '$pricePerUnit' },
          currency: { $first: '$currency' },
          date: { $first: '$date' },
          isInsurancePrice: { $first: '$isInsurancePrice' },
        },
      },
      { $sort: { latestPricePerUnit: 1 } },
    ]).exec();

    return results.map((r) => ({
      storeId: r._id as string,
      storeName: r.storeName as string,
      latestPrice: r.latestPrice as number,
      latestPricePerUnit: r.latestPricePerUnit as number,
      currency: r.currency as string,
      date: r.date as Date,
      isInsurancePrice: r.isInsurancePrice as boolean,
    }));
  }

  public async getLatestForMedicine(householdId: string, medicineId: string, storeId?: string) {
    const filter: Record<string, unknown> = { householdId, medicineId };
    if (storeId) filter['storeId'] = storeId;
    return MedicinePriceModel.findOne(filter).sort({ date: -1 }).lean().exec();
  }

  public async getAnalytics(householdId: string, query: MedicinePriceAnalyticsQueryInput) {
    const dateFormat =
      query.period === 'month' ? '%Y-%m' : query.period === 'quarter' ? '%Y-Q%q' : '%Y';

    const [spendingOverTime, topBySpending, spendingByStore] = await Promise.all([
      MedicinePriceModel.aggregate([
        { $match: { householdId } },
        {
          $group: {
            _id: { $dateToString: { format: dateFormat, date: '$date' } },
            total: { $sum: '$price' },
          },
        },
        { $sort: { _id: 1 } },
        { $project: { _id: 0, period: '$_id', total: 1 } },
      ]).exec(),

      MedicinePriceModel.aggregate([
        { $match: { householdId } },
        {
          $group: {
            _id: '$medicineId',
            medicineName: { $first: '$medicineName' },
            totalSpent: { $sum: '$price' },
            avgPricePerUnit: { $avg: '$pricePerUnit' },
          },
        },
        { $sort: { totalSpent: -1 } },
        { $limit: 10 },
        {
          $project: {
            _id: 0,
            medicineId: '$_id',
            medicineName: 1,
            totalSpent: 1,
            avgPricePerUnit: 1,
          },
        },
      ]).exec(),

      MedicinePriceModel.aggregate([
        { $match: { householdId } },
        {
          $group: {
            _id: '$storeId',
            storeName: { $first: '$storeName' },
            totalSpent: { $sum: '$price' },
            purchaseCount: { $sum: 1 },
          },
        },
        { $sort: { totalSpent: -1 } },
        { $project: { _id: 0, storeId: '$_id', storeName: 1, totalSpent: 1, purchaseCount: 1 } },
      ]).exec(),
    ]);

    // Price alerts: medicines where the most recent price is >10% higher than the previous
    const priceAlerts = await MedicinePriceModel.aggregate([
      { $match: { householdId } },
      { $sort: { medicineId: 1, storeId: 1, date: -1 } },
      {
        $group: {
          _id: { medicineId: '$medicineId', storeId: '$storeId' },
          medicineName: { $first: '$medicineName' },
          storeName: { $first: '$storeName' },
          prices: { $push: '$pricePerUnit' },
        },
      },
      { $match: { 'prices.1': { $exists: true } } },
      {
        $addFields: {
          currentPrice: { $arrayElemAt: ['$prices', 0] },
          previousPrice: { $arrayElemAt: ['$prices', 1] },
        },
      },
      {
        $addFields: {
          changePercent: {
            $multiply: [
              { $divide: [{ $subtract: ['$currentPrice', '$previousPrice'] }, '$previousPrice'] },
              100,
            ],
          },
        },
      },
      { $match: { changePercent: { $gt: 10 } } },
      {
        $project: {
          _id: 0,
          medicineId: '$_id.medicineId',
          medicineName: 1,
          storeName: 1,
          previousPrice: 1,
          currentPrice: 1,
          changePercent: 1,
        },
      },
    ]).exec();

    return {
      spendingOverTime: spendingOverTime as { period: string; total: number }[],
      topBySpending: topBySpending as {
        medicineId: string;
        medicineName: string;
        totalSpent: number;
        avgPricePerUnit: number;
      }[],
      spendingByStore: spendingByStore as {
        storeId: string;
        storeName: string;
        totalSpent: number;
        purchaseCount: number;
      }[],
      priceAlerts: priceAlerts as {
        medicineId: string;
        medicineName: string;
        storeName: string;
        previousPrice: number;
        currentPrice: number;
        changePercent: number;
      }[],
    };
  }
}
