using Predictalytics.Domain.Enums;
using Predictalytics.Infrastructure.Helpers;
using Xunit;

namespace Predictalytics.Application.Tests.Services;

/// <summary>
/// Tests for the keyword-based market classification (added 2026-07-11).
/// The Gamma /markets endpoint carries neither a category field nor event tags,
/// so for on-demand fetched markets the QUESTION TEXT is often the only signal —
/// the mapper must classify from it. Short tokens must match on word boundaries
/// ("eth" must not hit inside "whether", "pop" not inside "popular").
/// </summary>
public class MarketCategoryMapperTests
{
    [Theory]
    // Question-text-only classification (the on-demand path has no tags):
    [InlineData("", "", "Will the Lakers win the NBA Finals?", MarketCategory.Sports)]
    [InlineData("", "", "Ethereum above $1,850 on July 10?", MarketCategory.Crypto)]
    [InlineData("", "", "Will Trump win the 2028 presidential election?", MarketCategory.Politics)]
    [InlineData("", "", "Fed rate cut in September?", MarketCategory.Economy)]
    [InlineData("", "", "Will SpaceX launch Starship this quarter?", MarketCategory.Science)]
    [InlineData("", "", "Russia-Ukraine ceasefire before August?", MarketCategory.GlobalNews)]
    [InlineData("", "", "New Rihanna album before GTA VI?", MarketCategory.PopCulture)]
    // Tag-based classification still works:
    [InlineData("", "Sports, NBA", "", MarketCategory.Sports)]
    [InlineData("", "Crypto", "", MarketCategory.Crypto)]
    // No signal at all → Other:
    [InlineData("", "", "Something entirely unclassifiable happens?", MarketCategory.Other)]
    public void Map_ClassifiesFromAvailableSignals(string rawCategory, string tags, string question, MarketCategory expected)
    {
        var (category, _) = MarketCategoryMapper.Map(rawCategory, tags, question);
        Assert.Equal(expected, category);
    }

    [Fact]
    public void Map_ShortTokens_RequireWordBoundaries()
    {
        // "whether" contains the substring "eth" — must NOT classify as Crypto.
        var (category, _) = MarketCategoryMapper.Map("", "", "Whether the government acts by Friday?");
        Assert.NotEqual(MarketCategory.Crypto, category);

        // "popular vote" contains the substring "pop" — must be Politics ("vote"),
        // not PopCulture.
        var (category2, _) = MarketCategoryMapper.Map("", "", "Popular vote winner in Michigan?");
        Assert.Equal(MarketCategory.Politics, category2);
    }

    [Fact]
    public void Map_Subcategory_PrefersFirstTagOverFallback()
    {
        var (_, subcategory) = MarketCategoryMapper.Map("", "NBA, Basketball", "Lakers to win?");
        Assert.Equal("NBA", subcategory);
    }

    [Fact]
    public void Map_CanonicalTag_WinsOverHeuristics()
    {
        // "Politics" is a canonical tag, even though it's last in tags, it should map to Politics.
        var (category, subcategory) = MarketCategoryMapper.Map("", "Ethiopia, Elections, Politics", "Next PM of Ethiopia?");
        Assert.Equal(MarketCategory.Politics, category);
        Assert.Equal("Ethiopia", subcategory);
    }

    [Fact]
    public void Map_Subcategory_FiltersNoiseAndCategorySelf()
    {
        // Category is Sports.
        // "Sports" should be skipped as subcategory because it is the category itself.
        // "Hide From New" and "2025 Predictions" should be skipped as noise tags.
        // "Soccer" should be picked.
        var (category, subcategory) = MarketCategoryMapper.Map("", "Hide From New, Sports, 2025 Predictions, Soccer, FIFA World Cup", "Lakers to win?");
        Assert.Equal(MarketCategory.Sports, category);
        Assert.Equal("Soccer", subcategory);
    }

    [Fact]
    public void Map_Subcategory_EmptyNormalization()
    {
        // When all tags are noise or category names, subcategory should normalize to empty string.
        var (category, subcategory) = MarketCategoryMapper.Map("", "Sports, Hide From New, 2026", "Lakers to win?");
        Assert.Equal(MarketCategory.Sports, category);
        Assert.Equal(string.Empty, subcategory);
    }
}
