// Matching Service for Marketplace
// Matches buyers with sellers based on preferences and listings

import { Listing, ListingCategory, ListingStatus } from './types';
import { listingStore, sellerProfileStore } from './store';

export interface BuyerPreferences {
  categories: ListingCategory[];
  maxPrice?: number;
  preferredLocation?: {
    lat: number;
    lng: number;
    maxDistanceKm: number;
  };
  preferredTags?: string[];
  preferVerifiedSellers?: boolean;
}

export interface MatchResult {
  listing: Listing;
  score: number;
  matchReasons: string[];
}

export class MatchingService {
  /**
   * Find listings that match buyer preferences
   */
  async findMatchesForBuyer(
    buyerId: string,
    preferences: BuyerPreferences,
    limit: number = 10
  ): Promise<MatchResult[]> {
    const activeListings = listingStore
      .getAll()
      .filter(l => l.status === ListingStatus.ACTIVE && l.sellerId !== buyerId);

    const matches: MatchResult[] = [];

    for (const listing of activeListings) {
      const { score, reasons } = this.calculateMatchScore(listing, preferences);

      if (score > 0) {
        matches.push({
          listing,
          score,
          matchReasons: reasons,
        });
      }
    }

    // Sort by score descending
    matches.sort((a, b) => b.score - a.score);

    return matches.slice(0, limit);
  }

  /**
   * Find buyers who might be interested in a listing
   * (In a real system, this would query user preferences)
   */
  async findPotentialBuyers(listingId: string): Promise<string[]> {
    // Placeholder - would match against stored buyer preferences
    // For now, return empty array as we don't have buyer preference storage
    return [];
  }

  /**
   * Get recommended listings for a user based on their history
   */
  async getRecommendedListings(
    userId: string,
    limit: number = 8
  ): Promise<Listing[]> {
    // Get user's purchase history and viewed listings
    // For now, return featured listings as recommendations
    const activeListings = listingStore
      .getAll()
      .filter(l => l.status === ListingStatus.ACTIVE && l.sellerId !== userId);

    // Sort by a combination of view count and recency
    return activeListings
      .sort((a, b) => {
        const aScore = a.viewCount * 0.7 + this.recencyScore(a.createdAt) * 0.3;
        const bScore = b.viewCount * 0.7 + this.recencyScore(b.createdAt) * 0.3;
        return bScore - aScore;
      })
      .slice(0, limit);
  }

  /**
   * Find sellers with good ratings in a category
   */
  async findTopSellersInCategory(
    category: ListingCategory,
    limit: number = 5
  ): Promise<{ userId: string; displayName: string; rating: number; listingCount: number }[]> {
    // Get all active listings in category
    const categoryListings = listingStore
      .getAll()
      .filter(l => l.status === ListingStatus.ACTIVE && l.category === category);

    // Group by seller
    const sellerStats: Record<string, { count: number; sellerId: string }> = {};

    for (const listing of categoryListings) {
      if (!sellerStats[listing.sellerId]) {
        sellerStats[listing.sellerId] = { count: 0, sellerId: listing.sellerId };
      }
      sellerStats[listing.sellerId].count++;
    }

    // Get seller profiles and sort by rating
    const topSellers = Object.values(sellerStats)
      .map(stat => {
        const profile = sellerProfileStore.getByUserId(stat.sellerId);
        return {
          userId: stat.sellerId,
          displayName: profile?.displayName || 'Unknown Seller',
          rating: profile?.rating || 0,
          listingCount: stat.count,
        };
      })
      .sort((a, b) => b.rating - a.rating)
      .slice(0, limit);

    return topSellers;
  }

  /**
   * Calculate match score between a listing and buyer preferences
   */
  private calculateMatchScore(
    listing: Listing,
    preferences: BuyerPreferences
  ): { score: number; reasons: string[] } {
    let score = 0;
    const reasons: string[] = [];

    // Category match (highest weight)
    if (preferences.categories.includes(listing.category)) {
      score += 40;
      reasons.push(`Matches preferred category: ${listing.category}`);
    }

    // Price within budget
    if (preferences.maxPrice && listing.price <= preferences.maxPrice) {
      score += 20;
      reasons.push('Within budget');
    }

    // Location match
    if (preferences.preferredLocation && listing.location) {
      const distance = this.calculateDistance(
        preferences.preferredLocation.lat,
        preferences.preferredLocation.lng,
        listing.location.lat,
        listing.location.lng
      );

      if (distance <= preferences.preferredLocation.maxDistanceKm) {
        score += 25;
        reasons.push(`Nearby (${Math.round(distance)}km away)`);
      }
    }

    // Tag matches
    if (preferences.preferredTags && preferences.preferredTags.length > 0) {
      const matchingTags = listing.tags.filter(tag =>
        preferences.preferredTags!.includes(tag.toLowerCase())
      );
      if (matchingTags.length > 0) {
        score += matchingTags.length * 5;
        reasons.push(`Matching tags: ${matchingTags.join(', ')}`);
      }
    }

    // Verified seller preference
    if (preferences.preferVerifiedSellers) {
      const profile = sellerProfileStore.getByUserId(listing.sellerId);
      if (profile?.verified) {
        score += 10;
        reasons.push('Verified seller');
      }
    }

    return { score, reasons };
  }

  /**
   * Calculate recency score (0-100, higher for more recent)
   */
  private recencyScore(date: Date): number {
    const now = Date.now();
    const created = date.getTime();
    const daysOld = (now - created) / (1000 * 60 * 60 * 24);

    // Exponential decay: 100 for today, ~37 for 7 days, ~14 for 14 days
    return 100 * Math.exp(-daysOld / 10);
  }

  /**
   * Calculate distance between two points (Haversine formula)
   */
  private calculateDistance(
    lat1: number,
    lng1: number,
    lat2: number,
    lng2: number
  ): number {
    const R = 6371; // Earth's radius in km
    const dLat = this.toRad(lat2 - lat1);
    const dLng = this.toRad(lng2 - lng1);
    const a =
      Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(this.toRad(lat1)) *
        Math.cos(this.toRad(lat2)) *
        Math.sin(dLng / 2) *
        Math.sin(dLng / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
  }

  private toRad(deg: number): number {
    return deg * (Math.PI / 180);
  }
}

// Export singleton instance
export const matchingService = new MatchingService();
