import fs from 'fs';
import path from 'path';
import { Listing } from '../types';
import { INITIAL_LISTINGS } from '../data/initialData';
import { ServerTransaction } from './payments/types';

class ServerListingsStore {
  private listings: Map<string, Listing> = new Map();
  private listingsDbPath: string = path.join(process.cwd(), 'data', 'listings_db.json');

  constructor() {
    this.loadFromDisk();
  }

  private loadFromDisk(): void {
    try {
      if (fs.existsSync(this.listingsDbPath)) {
        const raw = fs.readFileSync(this.listingsDbPath, 'utf-8');
        const data = JSON.parse(raw);
        if (Array.isArray(data.listings) && data.listings.length > 0) {
          this.listings.clear();
          data.listings.forEach((item: Listing) => {
            if (item && item.id) {
              this.listings.set(item.id, item);
            }
          });
          return;
        }
      }
    } catch (e) {
      console.warn('Could not read listings_db.json, seeding from INITIAL_LISTINGS:', e);
    }

    // Initialize with default demo listings
    INITIAL_LISTINGS.forEach(item => {
      this.listings.set(item.id, { ...item });
    });
    this.saveToDisk();
  }

  private saveToDisk(): void {
    try {
      const dir = path.dirname(this.listingsDbPath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      const data = {
        version: 1,
        total: this.listings.size,
        updatedAt: new Date().toISOString(),
        listings: Array.from(this.listings.values()),
      };
      fs.writeFileSync(this.listingsDbPath, JSON.stringify(data, null, 2), 'utf-8');
    } catch (e) {
      console.error('CRITICAL: Failed to persist listings_db.json to disk:', e);
    }
  }

  /**
   * Exact Ranking Logic Specification:
   * Rule 1: Higher bid amount ranks higher (ORDER BY current_bid DESC).
   * Rule 2: If equal bid amount, the user/listing who reached that bid FIRST ranks higher (ORDER BY last_bid_at ASC).
   * Tie-breaker: Deterministic fallback by listing ID.
   */
  public sortListings(list: Listing[]): Listing[] {
    return [...list].sort((a, b) => {
      // 1. Higher bid amount ranks higher (current_bid DESC)
      if (b.current_bid !== a.current_bid) {
        return b.current_bid - a.current_bid;
      }
      // 2. Same bid amount -> earlier verified/accepted bid timestamp ranks higher (last_bid_at ASC)
      const timeA = new Date(a.last_bid_at).getTime();
      const timeB = new Date(b.last_bid_at).getTime();
      if (timeA !== timeB) {
        return timeA - timeB;
      }
      // Deterministic tie-breaker
      return a.id.localeCompare(b.id);
    });
  }

  /**
   * Computes category ranks independently for all active listings.
   * Returns a map of listingId -> category_rank (1..N within each category)
   */
  public getCategoryRankMap(): Record<string, number> {
    const active = Array.from(this.listings.values()).filter(l => l.is_active);
    const byCategory: Record<string, Listing[]> = {};

    active.forEach(item => {
      if (!byCategory[item.category_id]) {
        byCategory[item.category_id] = [];
      }
      byCategory[item.category_id].push(item);
    });

    const rankMap: Record<string, number> = {};
    Object.keys(byCategory).forEach(catId => {
      const sorted = this.sortListings(byCategory[catId]);
      sorted.forEach((item, index) => {
        rankMap[item.id] = index + 1;
      });
    });

    return rankMap;
  }

  /**
   * Server-side Query API Endpoint Handler:
   * 1. Filters active listings
   * 2. Filters by category / time filter / search query
   * 3. Sorts complete list using `current_bid DESC, last_bid_at ASC`
   * 4. Assigns rank and category_rank BEFORE pagination
   * 5. Paginates complete calculated rankings into 50 per page
   */
  public getRankedListings(options: {
    categoryId?: string;
    timeFilter?: string;
    searchQuery?: string;
    page?: number;
    limit?: number;
  }) {
    const { categoryId, timeFilter, searchQuery, page = 1, limit = 50 } = options;

    let active = Array.from(this.listings.values()).filter(l => l.is_active);

    // Compute category ranks for ALL active listings prior to filtering
    const categoryRankMap = this.getCategoryRankMap();

    // 1. Time Filter
    if (timeFilter === 'today') {
      const now = new Date();
      const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
      active = active.filter(item => {
        const itemDate = new Date(item.last_bid_at).getTime();
        return itemDate >= startOfDay || item.current_bid >= 3000;
      });
    }

    // 2. Category Filter (Independent Category Ranking)
    if (categoryId && categoryId !== 'all') {
      active = active.filter(item => item.category_id === categoryId);
    }

    // 3. Search Query Filter
    if (searchQuery && searchQuery.trim()) {
      const q = searchQuery.trim().toLowerCase();
      active = active.filter(item =>
        item.name.toLowerCase().includes(q) ||
        (item.category_name && item.category_name.toLowerCase().includes(q)) ||
        (item.url && item.url.toLowerCase().includes(q)) ||
        (item.facebook_url && item.facebook_url.toLowerCase().includes(q))
      );
    }

    // 4. Sort according to EXACT ranking rules: current_bid DESC, last_bid_at ASC
    const sorted = this.sortListings(active);

    // 5. Assign rank 1..N to the full sorted set BEFORE pagination
    const ranked = sorted.map((item, index) => ({
      ...item,
      rank: index + 1,
      category_rank: categoryRankMap[item.id] || (index + 1),
    }));

    // 6. Divide into pages (50 per page) AFTER complete ranking calculation
    const total = ranked.length;
    const totalPages = Math.ceil(total / limit) || 1;
    const currentPage = Math.max(1, Math.min(page, totalPages));
    const startIndex = (currentPage - 1) * limit;
    const paginatedListings = ranked.slice(startIndex, startIndex + limit);

    return {
      success: true,
      listings: paginatedListings,
      allRankedListings: ranked,
      categoryRankMap,
      total,
      page: currentPage,
      limit,
      totalPages,
    };
  }

  /**
   * Atomically process a verified bid/payment on the server.
   * Assigns exact server timestamp for `last_bid_at` when accepted.
   */
  public processVerifiedBid(txn: ServerTransaction): Listing {
    const serverTimestamp = txn.paid_at || txn.updated_at || new Date().toISOString();
    const bidAmount = Number(txn.amount);

    // Only update if exact listing_id explicitly exists in store
    let existing: Listing | undefined;
    if (txn.listing_id && this.listings.has(txn.listing_id)) {
      existing = this.listings.get(txn.listing_id);
    }

    if (existing) {
      // Update existing listing with new verified bid amount and server timestamp
      const updatedListing: Listing = {
        ...existing,
        name: txn.listing_name || existing.name,
        image_url: txn.listing_image || existing.image_url,
        facebook_url: txn.facebook_url || existing.facebook_url || txn.listing_url || existing.url || '',
        website_url: txn.website_url || existing.website_url || '',
        url: txn.listing_url || existing.url,
        current_bid: Math.max(existing.current_bid, bidAmount),
        category_id: txn.category_id || existing.category_id,
        updated_at: serverTimestamp,
        last_bid_at: serverTimestamp, // RECORD EXACT SERVER TIMESTAMP FOR ACCEPTED BID
      };
      this.listings.set(existing.id, updatedListing);
      this.saveToDisk();
      return updatedListing;
    } else {
      // Create new listing record with exact server timestamp
      const newListingId = txn.listing_id || `list_${Date.now()}_${Math.floor(1000 + Math.random() * 9000)}`;
      const finalFb = txn.facebook_url || txn.listing_url || 'https://facebook.com';
      const finalWeb = txn.website_url || '';

      const newListing: Listing = {
        id: newListingId,
        name: txn.listing_name.trim(),
        image_url: txn.listing_image || 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=200&auto=format&fit=crop&q=80',
        facebook_url: finalFb,
        website_url: finalWeb,
        url: txn.listing_url || finalFb,
        category_id: txn.category_id || 'cat_biz',
        category_name: txn.category_name || 'Business',
        current_bid: bidAmount,
        clicks: 0,
        hearts: 0,
        is_active: true,
        created_at: serverTimestamp,
        updated_at: serverTimestamp,
        last_bid_at: serverTimestamp, // RECORD EXACT SERVER TIMESTAMP FOR ACCEPTED BID
      };

      this.listings.set(newListingId, newListing);
      this.saveToDisk();
      return newListing;
    }
  }

  public getAllListings(): Listing[] {
    return this.sortListings(Array.from(this.listings.values()));
  }

  public getListing(id: string): Listing | undefined {
    return this.listings.get(id);
  }

  public saveListing(listingData: Partial<Listing>): Listing {
    const serverTimestamp = new Date().toISOString();
    let listing: Listing;

    if (listingData.id && this.listings.has(listingData.id)) {
      const existing = this.listings.get(listingData.id)!;
      listing = {
        ...existing,
        ...listingData,
        updated_at: serverTimestamp,
        // Update last_bid_at if bid amount changed or if explicitly provided
        last_bid_at: (listingData.current_bid !== undefined && listingData.current_bid !== existing.current_bid)
          ? (listingData.last_bid_at || serverTimestamp)
          : (listingData.last_bid_at || existing.last_bid_at),
      };
    } else {
      const newId = listingData.id || `list_${Date.now()}_${Math.floor(1000 + Math.random() * 9000)}`;
      listing = {
        id: newId,
        name: listingData.name || 'New Listing',
        image_url: listingData.image_url || 'https://images.unsplash.com/photo-1542838132-92c53300491e?w=200&auto=format&fit=crop&q=80',
        facebook_url: listingData.facebook_url || listingData.url || 'https://facebook.com',
        website_url: listingData.website_url || '',
        url: listingData.url || listingData.facebook_url || 'https://facebook.com',
        category_id: listingData.category_id || 'cat_biz',
        category_name: listingData.category_name || 'Business',
        current_bid: Number(listingData.current_bid) || 100,
        clicks: Number(listingData.clicks) || 0,
        hearts: Number(listingData.hearts) || 0,
        is_active: listingData.is_active !== undefined ? listingData.is_active : true,
        created_at: listingData.created_at || serverTimestamp,
        updated_at: serverTimestamp,
        last_bid_at: listingData.last_bid_at || serverTimestamp,
      };
    }

    this.listings.set(listing.id, listing);
    this.saveToDisk();
    return listing;
  }

  public deleteListing(id: string): boolean {
    const deleted = this.listings.delete(id);
    if (deleted) {
      this.saveToDisk();
    }
    return deleted;
  }

  public incrementClicks(id: string): Listing | null {
    const listing = this.listings.get(id);
    if (!listing) return null;
    listing.clicks += 1;
    this.listings.set(id, listing);
    this.saveToDisk();
    return listing;
  }

  public incrementHearts(id: string): Listing | null {
    const listing = this.listings.get(id);
    if (!listing) return null;
    listing.hearts += 1;
    this.listings.set(id, listing);
    this.saveToDisk();
    return listing;
  }
}

export const serverListingsStore = new ServerListingsStore();
