import { Injectable, Inject, NotFoundException, Logger } from '@nestjs/common';
import { STORE_REPOSITORY, IStoreRepository } from '../../domain/repositories/store.repository.interface';
import { COUPON_REPOSITORY, ICouponRepository } from '../../domain/repositories/coupon.repository.interface';
import { ISSUER_REPOSITORY, IIssuerRepository } from '../../domain/repositories/issuer.repository.interface';
import { Store } from '../../domain/entities/store.entity';

export interface MerchantRedemptionStats {
  totalStores: number;
  activeStores: number;
  flaggedStores: number;
  totalCouponsIssued: number;
  totalCouponsSold: number;
  overallRedemptionRate: number;
}

export interface StoreRanking {
  storeId: string;
  storeName: string;
  issuerName: string;
  issuerId: string;
  address: string | null;
  status: string;
  estimatedRedemptions: number;
}

export interface RealtimeFeedItem {
  storeId: string;
  storeName: string;
  issuerName: string;
  couponName: string;
  couponId: string;
  type: string;
  timestamp: string;
}

@Injectable()
export class AdminMerchantService {
  private readonly logger = new Logger('AdminMerchantService');

  constructor(
    @Inject(STORE_REPOSITORY)
    private readonly storeRepo: IStoreRepository,
    @Inject(COUPON_REPOSITORY)
    private readonly couponRepo: ICouponRepository,
    @Inject(ISSUER_REPOSITORY)
    private readonly issuerRepo: IIssuerRepository,
  ) {}

  /**
   * Get merchant/store redemption statistics overview.
   */
  async getRedemptionStats(): Promise<MerchantRedemptionStats> {
    const totalStores = await this.storeRepo.count();
    const activeStores = await this.storeRepo.count({ status: 'active' });
    const flaggedStores = await this.storeRepo.count({ status: 'flagged' });

    const couponAgg = await this.couponRepo.getCouponSupplyAggregate();
    const totalIssued = couponAgg.totalCouponsIssued;
    const totalSold = couponAgg.totalCouponsSold;

    return {
      totalStores,
      activeStores,
      flaggedStores,
      totalCouponsIssued: totalIssued,
      totalCouponsSold: totalSold,
      overallRedemptionRate:
        totalIssued > 0
          ? Math.round((totalSold / totalIssued) * 10000) / 100
          : 0,
    };
  }

  /**
   * Rank stores by estimated redemption volume.
   */
  async getStoreRanking(limit: number = 20): Promise<StoreRanking[]> {
    const stores = await this.storeRepo.findTopStores(limit);
    if (stores.length === 0) return [];

    const issuerIds = [...new Set(stores.map((s) => s.issuerId))];

    // Get issuer names
    const issuers = await this.issuerRepo.findByIds(issuerIds);
    const issuerMap = new Map(issuers.map((i) => [i.id, i.companyName]));

    // Get total sold per issuer
    const soldPerIssuer = await this.couponRepo.getSoldPerIssuer(issuerIds);
    const soldMap = new Map(soldPerIssuer.map((r) => [r.issuerId, r.totalSold]));

    // Build ranking
    const ranked = stores.map((store) => ({
      storeId: store.id,
      storeName: store.name,
      issuerName: issuerMap.get(store.issuerId) || 'Unknown',
      issuerId: store.issuerId,
      address: store.address,
      status: store.status,
      estimatedRedemptions: soldMap.get(store.issuerId) || 0,
    }));

    // Sort by estimated redemptions descending
    ranked.sort((a, b) => b.estimatedRedemptions - a.estimatedRedemptions);

    return ranked.slice(0, limit);
  }

  /**
   * Simulated real-time feed of recent redemption activity.
   */
  async getRealtimeFeed(limit: number = 50): Promise<RealtimeFeedItem[]> {
    const recentCoupons = await this.couponRepo.getRecentlySoldCoupons(limit);
    if (recentCoupons.length === 0) return [];

    const issuerIds = [...new Set(recentCoupons.map((c) => c.issuerId))];

    // Get issuers and their stores
    const issuers = await this.issuerRepo.findByIds(issuerIds);
    const issuerMap = new Map(issuers.map((i) => [i.id, i.companyName]));

    const stores = await this.storeRepo.findByIssuerIds(issuerIds);
    // Map issuer -> first store (for feed display)
    const storeByIssuer = new Map<string, Store>();
    stores.forEach((s) => {
      if (!storeByIssuer.has(s.issuerId)) {
        storeByIssuer.set(s.issuerId, s);
      }
    });

    return recentCoupons.map((coupon) => {
      const store = storeByIssuer.get(coupon.issuerId);
      return {
        storeId: store?.id || 'N/A',
        storeName: store?.name || 'No store',
        issuerName: issuerMap.get(coupon.issuerId) || 'Unknown',
        couponName: coupon.name,
        couponId: coupon.id,
        type: coupon.couponType,
        timestamp: coupon.updatedAt.toISOString(),
      };
    });
  }

  /**
   * Flag a store as abnormal for investigation.
   */
  async flagStore(storeId: string, reason: string): Promise<Store> {
    const store = await this.storeRepo.findById(storeId);
    if (!store) throw new NotFoundException('Store not found');

    store.status = 'flagged';
    this.logger.warn(`Store ${storeId} (${store.name}) flagged: ${reason}`);

    return this.storeRepo.save(store);
  }
}
