using AccountTracking.Api.Data;
using AccountTracking.Api.Models;
using AccountTracking.Api.Services;
using Microsoft.EntityFrameworkCore;

namespace AccountTracking.Api.Tests;

public class DashboardServiceTests
{
    private static AppDbContext CreateDb(params Transaction[] seed)
    {
        var opts = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;
        var db = new AppDbContext(opts);
        db.Transactions.AddRange(seed);
        db.SaveChanges();
        return db;
    }

    private static Transaction Tx(string date, decimal amount, decimal balance,
        string category = "Test", string txId = "") =>
        new()
        {
            BookingDate = DateOnly.Parse(date),
            Amount = amount,
            Balance = balance,
            Category = category,
            TransactionId = txId == "" ? Guid.NewGuid().ToString() : txId,
            Currency = "CZK",
            AccountNumber = "test"
        };

    // ── Summary ──────────────────────────────────────────────────────────────

    [Fact]
    public async Task Summary_YearOnly_ReturnsTotalsForWholeYear()
    {
        using var db = CreateDb(
            Tx("2025-01-10", -1000, 9000),
            Tx("2025-06-15", -2000, 7000),
            Tx("2025-03-01", 5000, 12000),
            Tx("2024-12-31", -500, 9500) // different year — excluded
        );
        var svc = new DashboardService(db);

        var result = await svc.GetSummaryAsync(2025, null);

        Assert.Equal(3000m, result.TotalSpent);   // 1000 + 2000
        Assert.Equal(5000m, result.TotalIncome);
    }

    [Fact]
    public async Task Summary_YearAndMonth_ReturnsTotalsForMonth()
    {
        using var db = CreateDb(
            Tx("2025-03-10", -1000, 9000),
            Tx("2025-03-20", -500, 8500),
            Tx("2025-04-01", -800, 7700) // different month — excluded
        );
        var svc = new DashboardService(db);

        var result = await svc.GetSummaryAsync(2025, 3);

        Assert.Equal(1500m, result.TotalSpent);
        Assert.Equal(0m, result.TotalIncome);
    }

    // ── SpendingByCategory ───────────────────────────────────────────────────

    [Fact]
    public async Task SpendingByCategory_ReturnsAbsoluteValuesSortedDescending()
    {
        using var db = CreateDb(
            Tx("2025-03-01", -100, 900, "A"),
            Tx("2025-03-02", -300, 600, "B"),
            Tx("2025-03-03", -200, 400, "A")
        );
        var svc = new DashboardService(db);

        var result = await svc.GetSpendingByCategoryAsync(2025, 3);

        Assert.Equal(2, result.Count);
        Assert.Equal("A", result[0].Category);
        Assert.Equal(300m, result[0].Total); // 100 + 200
        Assert.Equal("B", result[1].Category);
        Assert.Equal(300m, result[1].Total);
    }

    [Fact]
    public async Task SpendingByCategory_ExcludesPositiveAmounts()
    {
        using var db = CreateDb(
            Tx("2025-03-01", -500, 9500, "Potraviny"),
            Tx("2025-03-02", 200, 9700, "Potraviny")  // refund — excluded
        );
        var svc = new DashboardService(db);

        var result = await svc.GetSpendingByCategoryAsync(2025, 3);

        Assert.Single(result);
        Assert.Equal(500m, result[0].Total);
    }

    // ── MonthlyBalances ──────────────────────────────────────────────────────

    [Fact]
    public async Task MonthlyBalances_ReturnsLastBalancePerMonth()
    {
        using var db = CreateDb(
            Tx("2025-01-10", -100, 900),
            Tx("2025-01-20", -200, 700),  // last in Jan — use this balance
            Tx("2025-03-05", -150, 550)   // March — Feb omitted (no transactions)
        );
        var svc = new DashboardService(db);

        var result = await svc.GetMonthlyBalancesAsync(2025);

        Assert.Equal(2, result.Count);
        Assert.Equal(1, result[0].Month);
        Assert.Equal(700m, result[0].ClosingBalance);
        Assert.Equal(3, result[1].Month);
        Assert.Equal(550m, result[1].ClosingBalance);
    }

    // ── CumulativeSpending ───────────────────────────────────────────────────

    [Fact]
    public async Task CumulativeSpending_FillsGapsAndAccumulates()
    {
        using var db = CreateDb(
            Tx("2025-03-01", -100, 900),
            Tx("2025-03-03", -200, 700),  // gap on day 2
            Tx("2025-03-03", -50, 650)    // two transactions on same day
        );
        var svc = new DashboardService(db);

        var result = await svc.GetCumulativeSpendingAsync(2025, 3);

        // Day 1: 100, Day 2: filled = 100, Day 3: 100 + 250 = 350
        Assert.Equal(3, result.Count);
        Assert.Equal(1, result[0].Day); Assert.Equal(100m, result[0].CumulativeSpent);
        Assert.Equal(2, result[1].Day); Assert.Equal(100m, result[1].CumulativeSpent);
        Assert.Equal(3, result[2].Day); Assert.Equal(350m, result[2].CumulativeSpent);
    }

    [Fact]
    public async Task CumulativeSpending_ExcludesPositiveAmounts()
    {
        using var db = CreateDb(
            Tx("2025-03-01", -100, 900),
            Tx("2025-03-01", 500, 1400)   // income — not counted
        );
        var svc = new DashboardService(db);

        var result = await svc.GetCumulativeSpendingAsync(2025, 3);

        Assert.Single(result);
        Assert.Equal(100m, result[0].CumulativeSpent);
    }
}
