// Search Service for Marketplace
// Handles listing search and filtering

import {
  Listing,
  ListingStatus,
  SearchFilters,
  SearchResult,
  ListingCategory,
  MarketplaceStats,
} from './types';
import { listingStore } from './store';

export class SearchService {
  /**
   * Search listings with filters
   */
  async searchListings(filters: SearchFilters): Promise<SearchResult> {
    let listings = listingStore.getAll();

    // Only show active listings unless specific status requested
    if (filters.status) {
      listings = listings.filter(l => l.status === filters.status);
    } else {
      listings = listings.filter(l => l.status === ListingStatus.ACTIVE);
    }

    // Text search in title and description
    if (filters.query) {
      const query = filters.query.toLowerCase();
      listings = listings.filter(l =>
        l.title.toLowerCase().includes(query) ||
        l.description.toLowerCase().includes(query) ||
        l.tags.some(tag => tag.toLowerCase().includes(query))
      );
    }

    // Category filter
    if (filters.category) {
      listings = listings.filter(l => l.category === filters.category);
    }

    // Price range filter
    if (filters.minPrice !== undefined) {
      listings = listings.filter(l => l.price >= filters.minPrice!);
    }
    if (filters.maxPrice !== undefined) {
      listings = listings.filter(l => l.price <= filters.maxPrice!);
    }

    // Seller filter
    if (filters.sellerId) {
      listings = listings.filter(l => l.sellerId === filters.sellerId);
    }

    // Tags filter
    if (filters.tags && filters.tags.length > 0) {
      const filterTags = filters.tags.map(t => t.toLowerCase());
      listings = listings.filter(l =>
        filterTags.some(tag => l.tags.map(t => t.toLowerCase()).includes(tag))
      );
    }

    // Location filter (approximate distance)
    if (filters.location) {
      listings = listings.filter(l => {
        if (!l.location) return false;
        const distance = this.calculateDistance(
          filters.location!.lat,
          filters.location!.lng,
          l.location.lat,
          l.location.lng
        );
        return distance <= filters.location!.radiusKm;
      });
    }

    // Sort listings
    listings = this.sortListings(listings, filters.sortBy || 'date_desc', filters.query);

    // Pagination
    const page = filters.page || 1;
    const limit = filters.limit || 20;
    const total = listings.length;
    const startIndex = (page - 1) * limit;
    const paginatedListings = listings.slice(startIndex, startIndex + limit);

    return {
      listings: paginatedListings,
      total,
      page,
      limit,
      hasMore: startIndex + limit < total,
    };
  }

  /**
   * Get featured listings (most viewed active listings)
   */
  async getFeaturedListings(limit: number = 6): Promise<Listing[]> {
    return listingStore
      .getAll()
      .filter(l => l.status === ListingStatus.ACTIVE)
      .sort((a, b) => b.viewCount - a.viewCount)
      .slice(0, limit);
  }

  /**
   * Get recent listings
   */
  async getRecentListings(limit: number = 10): Promise<Listing[]> {
    return listingStore
      .getAll()
      .filter(l => l.status === ListingStatus.ACTIVE)
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
      .slice(0, limit);
  }

  /**
   * Get listings by category
   */
  async getListingsByCategory(
    category: ListingCategory,
    limit: number = 20
  ): Promise<Listing[]> {
    return listingStore
      .getAll()
      .filter(l => l.status === ListingStatus.ACTIVE && l.category === category)
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
      .slice(0, limit);
  }

  /**
   * Get similar listings based on category and tags
   */
  async getSimilarListings(listingId: string, limit: number = 4): Promise<Listing[]> {
    const listing = listingStore.getById(listingId);
    if (!listing) return [];

    return listingStore
      .getAll()
      .filter(l =>
        l.id !== listingId &&
        l.status === ListingStatus.ACTIVE &&
        (l.category === listing.category ||
          l.tags.some(tag => listing.tags.includes(tag)))
      )
      .sort((a, b) => {
        // Score based on matching tags
        const aScore = a.tags.filter(tag => listing.tags.includes(tag)).length;
        const bScore = b.tags.filter(tag => listing.tags.includes(tag)).length;
        return bScore - aScore;
      })
      .slice(0, limit);
  }

  /**
   * Get marketplace statistics
   */
  async getMarketplaceStats(): Promise<MarketplaceStats> {
    const allListings = listingStore.getAll();
    const activeListings = allListings.filter(l => l.status === ListingStatus.ACTIVE);
    const soldListings = allListings.filter(l => l.status === ListingStatus.SOLD);

    // Category counts
    const categoryCounts: Record<ListingCategory, number> = {
      [ListingCategory.SEEDS]: 0,
      [ListingCategory.SEEDLINGS]: 0,
      [ListingCategory.MATURE_PLANTS]: 0,
      [ListingCategory.CUTTINGS]: 0,
      [ListingCategory.PRODUCE]: 0,
      [ListingCategory.SUPPLIES]: 0,
    };

    activeListings.forEach(l => {
      categoryCounts[l.category]++;
    });

    // Top categories
    const topCategories = Object.entries(categoryCounts)
      .map(([category, count]) => ({
        category: category as ListingCategory,
        count
      }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 5);

    // Average price
    const averagePrice = activeListings.length > 0
      ? activeListings.reduce((sum, l) => sum + l.price, 0) / activeListings.length
      : 0;

    return {
      totalListings: allListings.length,
      activeListings: activeListings.length,
      totalSales: soldListings.length,
      totalOffers: 0, // Would be calculated from offer store
      categoryCounts,
      averagePrice: Math.round(averagePrice * 100) / 100,
      topCategories,
    };
  }

  /**
   * Get popular tags
   */
  async getPopularTags(limit: number = 20): Promise<{ tag: string; count: number }[]> {
    const tagCounts: Record<string, number> = {};

    listingStore
      .getAll()
      .filter(l => l.status === ListingStatus.ACTIVE)
      .forEach(l => {
        l.tags.forEach(tag => {
          tagCounts[tag] = (tagCounts[tag] || 0) + 1;
        });
      });

    return Object.entries(tagCounts)
      .map(([tag, count]) => ({ tag, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, limit);
  }

  /**
   * Sort listings based on sort option
   */
  private sortListings(
    listings: Listing[],
    sortBy: string,
    query?: string
  ): Listing[] {
    switch (sortBy) {
      case 'price_asc':
        return listings.sort((a, b) => a.price - b.price);
      case 'price_desc':
        return listings.sort((a, b) => b.price - a.price);
      case 'date_asc':
        return listings.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
      case 'date_desc':
        return listings.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
      case 'relevance':
        if (!query) {
          return listings.sort((a, b) => b.viewCount - a.viewCount);
        }
        // Simple relevance scoring based on title match
        return listings.sort((a, b) => {
          const aTitle = a.title.toLowerCase().includes(query.toLowerCase()) ? 1 : 0;
          const bTitle = b.title.toLowerCase().includes(query.toLowerCase()) ? 1 : 0;
          return bTitle - aTitle;
        });
      default:
        return listings;
    }
  }

  /**
   * 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 searchService = new SearchService();
