← back to Wine Finder Next

app/api/best-deal/route.ts

137 lines

import { NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'

const PRICE_HISTORY_FILE = '/root/Projects/wine-finder-next/data/price-history-5sec.jsonl'

interface WineEntry {
  timestamp: string
  name: string
  price: number
  rating: number | null
  vintage: number
  availability: string
  url: string
  image: string
  source: string
  searchQuery: string
  winery: string
  region: string
  ratingsCount: number
}

interface PriceStats {
  name: string
  winery: string
  vintage: number
  image: string
  url: string
  source: string
  region: string
  currentPrice: number
  lowestPrice: number
  highestPrice: number
  discount: number // percentage
  savings: number // dollar amount
  priceHistory: Array<{timestamp: string, price: number}>
  trackingSince: string
  lastUpdated: string
}

export async function GET() {
  try {
    // Read price history
    if (!fs.existsSync(PRICE_HISTORY_FILE)) {
      return NextResponse.json({ bestDeal: null, message: 'No price history available yet' })
    }

    const data = fs.readFileSync(PRICE_HISTORY_FILE, 'utf-8')
    const lines = data.trim().split('\n').filter(l => l)

    if (lines.length === 0) {
      return NextResponse.json({ bestDeal: null, message: 'No price data available' })
    }

    // Parse all entries
    const entries: WineEntry[] = lines.map(line => JSON.parse(line))

    // Group by wine (name + vintage + winery)
    const wineMap = new Map<string, WineEntry[]>()

    entries.forEach(entry => {
      const key = `${entry.name}|${entry.vintage}|${entry.winery}`
      if (!wineMap.has(key)) {
        wineMap.set(key, [])
      }
      wineMap.get(key)!.push(entry)
    })

    // Calculate stats for each wine
    const stats: PriceStats[] = []

    wineMap.forEach((entries, key) => {
      const sorted = entries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())
      const prices = entries.map(e => e.price)
      const lowestPrice = Math.min(...prices)
      const highestPrice = Math.max(...prices)
      const currentPrice = sorted[sorted.length - 1].price
      const latest = sorted[sorted.length - 1]

      // Calculate discount from highest to current
      const savings = highestPrice - currentPrice
      const discount = highestPrice > 0 ? ((savings / highestPrice) * 100) : 0

      stats.push({
        name: latest.name,
        winery: latest.winery,
        vintage: latest.vintage,
        image: latest.image,
        url: latest.url,
        source: latest.source,
        region: latest.region,
        currentPrice,
        lowestPrice,
        highestPrice,
        discount: Math.round(discount * 10) / 10,
        savings: Math.round(savings * 100) / 100,
        priceHistory: sorted.map(e => ({ timestamp: e.timestamp, price: e.price })),
        trackingSince: sorted[0].timestamp,
        lastUpdated: sorted[sorted.length - 1].timestamp
      })
    })

    // Find the best deal: SORT BY DOLLAR SAVINGS ONLY (not percentage)
    // This ensures we show "$50 saved" instead of "50% off $2"
    const bestDeal = stats
      .filter(s => s.savings > 0)
      .sort((a, b) => {
        // ONLY sort by dollar savings - highest $ saved wins
        return b.savings - a.savings
      })[0]

    // Also find: most expensive wine currently available (for display when no deals)
    const mostExpensive = stats.sort((a, b) => b.currentPrice - a.currentPrice)[0]

    // Find: biggest percentage discount (for comparison)
    const biggestPercentageOff = stats
      .filter(s => s.discount > 0)
      .sort((a, b) => b.discount - a.discount)[0]

    return NextResponse.json({
      bestDeal: bestDeal || null, // Highest $ savings
      mostExpensive: mostExpensive || null,
      biggestPercentageOff: biggestPercentageOff || null, // Highest % off
      totalWinesTracked: stats.length,
      totalPricePoints: entries.length,
      trackingStarted: entries[0]?.timestamp
    })

  } catch (error) {
    console.error('Error calculating best deal:', error)
    return NextResponse.json(
      { error: 'Failed to calculate best deal', details: error instanceof Error ? error.message : 'Unknown error' },
      { status: 500 }
    )
  }
}