Files
EbayListingTool/EbayListingTool/Services/PriceLookupService.cs

84 lines
2.8 KiB
C#

using System.Text.RegularExpressions;
using EbayListingTool.Models;
namespace EbayListingTool.Services;
public record PriceSuggestion(decimal Price, string Source, string Label);
/// <summary>
/// Layered price suggestion: eBay live data → own listing history → AI estimate.
/// Returns the first source that produces a result, labelled so the UI can show
/// where the suggestion came from.
/// </summary>
public class PriceLookupService
{
private readonly EbayPriceResearchService _ebay;
private readonly SavedListingsService _history;
private readonly AiAssistantService _ai;
private static readonly Regex PriceRegex =
new(@"PRICE:\s*(\d+\.?\d*)", RegexOptions.IgnoreCase);
public PriceLookupService(
EbayPriceResearchService ebay,
SavedListingsService history,
AiAssistantService ai)
{
_ebay = ebay;
_history = history;
_ai = ai;
}
public async Task<PriceSuggestion?> GetSuggestionAsync(SavedListing listing)
{
// 1. eBay live listings
try
{
var result = await _ebay.GetLivePricesAsync(listing.Title);
if (result.HasSuggestion)
return new PriceSuggestion(
result.Suggested,
"ebay",
$"eBay suggests \u00A3{result.Suggested:F2} (from {result.Count} listings)");
}
catch { /* eBay unavailable — fall through */ }
// 2. Own saved listing history — same category, at least 2 data points
var sameCat = _history.Listings
.Where(l => l.Id != listing.Id
&& !string.IsNullOrWhiteSpace(l.Category)
&& l.Category.Equals(listing.Category, StringComparison.OrdinalIgnoreCase)
&& l.Price > 0)
.Select(l => l.Price)
.ToList();
if (sameCat.Count >= 2)
{
var avg = Math.Round(sameCat.Average(), 2);
return new PriceSuggestion(
avg,
"history",
$"Your avg for {listing.Category}: \u00A3{avg:F2} ({sameCat.Count} listings)");
}
// 3. AI estimate
try
{
var response = await _ai.SuggestPriceAsync(listing.Title, listing.ConditionNotes);
var match = PriceRegex.Match(response);
if (match.Success
&& decimal.TryParse(match.Groups[1].Value,
System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture,
out var price)
&& price > 0)
{
return new PriceSuggestion(price, "ai", $"AI estimate: \u00A3{price:F2}");
}
}
catch { /* AI unavailable */ }
return null;
}
}