← back to Wine Finder Next
lib/services/realHistoryService.ts
110 lines
/**
* Real Historical Price Data Service
* Reads actual price tracking data from JSONL file
*/
import fs from 'fs/promises';
import path from 'path';
export interface RealPricePoint {
timestamp: string;
name: string;
price: number;
source: string;
winery?: string;
region?: string;
url?: string;
}
export interface LastSaleInfo {
price: number;
source: string;
date: string;
daysAgo: number;
}
export async function getRealPriceHistory(wineName: string): Promise<RealPricePoint[]> {
try {
const dataDir = path.join(process.cwd(), 'data');
const priceHistoryFile = path.join(dataDir, 'price-history.jsonl');
const fileContent = await fs.readFile(priceHistoryFile, 'utf-8');
const lines = fileContent.trim().split('\n');
const history: RealPricePoint[] = [];
for (const line of lines) {
try {
const entry = JSON.parse(line);
// Fuzzy match wine name
const nameMatch = entry.name.toLowerCase().includes(wineName.toLowerCase()) ||
wineName.toLowerCase().includes(entry.name.toLowerCase());
if (nameMatch) {
history.push({
timestamp: entry.timestamp,
name: entry.name,
price: entry.price,
source: entry.source || 'Unknown',
winery: entry.winery,
region: entry.region,
url: entry.url,
});
}
} catch (e) {
// Skip invalid JSON lines
continue;
}
}
// Sort by timestamp (oldest first)
return history.sort((a, b) =>
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
);
} catch (error) {
console.error('Error reading real price history:', error);
return [];
}
}
export async function getLastSaleInfo(wineName: string): Promise<LastSaleInfo | null> {
const history = await getRealPriceHistory(wineName);
if (history.length === 0) return null;
// Get most recent entry
const lastSale = history[history.length - 1];
const lastSaleDate = new Date(lastSale.timestamp);
const now = new Date();
const daysAgo = Math.floor((now.getTime() - lastSaleDate.getTime()) / (1000 * 60 * 60 * 24));
return {
price: lastSale.price,
source: lastSale.source,
date: lastSale.timestamp,
daysAgo,
};
}
export async function getAveragePriceBySource(wineName: string): Promise<Record<string, number>> {
const history = await getRealPriceHistory(wineName);
const pricesBySource: Record<string, number[]> = {};
history.forEach(point => {
if (!pricesBySource[point.source]) {
pricesBySource[point.source] = [];
}
pricesBySource[point.source].push(point.price);
});
const averages: Record<string, number> = {};
for (const [source, prices] of Object.entries(pricesBySource)) {
const avg = prices.reduce((sum, p) => sum + p, 0) / prices.length;
averages[source] = parseFloat(avg.toFixed(2));
}
return averages;
}