using Microsoft.Extensions.Logging;
using Service.Transaction;

namespace WebApi.Test.Transaction;

public class TransactionPeriodicServiceTest : BaseTest
{
    private readonly ITransactionPeriodicRepository _periodicRepository = Substitute.For<ITransactionPeriodicRepository>();
    private readonly ITransactionRecordRepository _transactionRepository = Substitute.For<ITransactionRecordRepository>();
    private readonly IMessageRecordRepository _messageRepository = Substitute.For<IMessageRecordRepository>();
    private readonly ILogger<TransactionPeriodicService> _logger = Substitute.For<ILogger<TransactionPeriodicService>>();
    private readonly ITransactionPeriodicService _service;

    public TransactionPeriodicServiceTest()
    {
        _service = new TransactionPeriodicService(
            _periodicRepository,
            _transactionRepository,
            _messageRepository,
            _logger
        );
    }

    [Fact]
    public async Task ExecutePeriodicBillsAsync_每日账单()
    {
        // Arrange
        var today = DateTime.Today;
        var periodicBill = new TransactionPeriodic
        {
            Id = 1,
            PeriodicType = PeriodicType.Daily,
            Amount = 100m,
            Type = TransactionType.Expense,
            Classify = "餐饮",
            Reason = "每日餐费",
            IsEnabled = true,
            LastExecuteTime = today.AddDays(-1),
            NextExecuteTime = today
        };

        _periodicRepository.GetPendingPeriodicBillsAsync().Returns(new[] { periodicBill });
        _transactionRepository.AddAsync(Arg.Any<TransactionRecord>()).Returns(Task.FromResult(true));
        _messageRepository.AddAsync(Arg.Any<MessageRecord>()).Returns(Task.FromResult(true));
        _periodicRepository.UpdateExecuteTimeAsync(Arg.Any<long>(), Arg.Any<DateTime>(), Arg.Any<DateTime>())
            .Returns(Task.FromResult(true));

        // Act
        await _service.ExecutePeriodicBillsAsync();

        // Assert
        // Service inserts Amount directly from periodicBill.Amount (100 is positive)
        await _transactionRepository.Received(1).AddAsync(Arg.Is<TransactionRecord>(t =>
            t.Amount == 100m && 
            t.Type == TransactionType.Expense &&
            t.Classify == "餐饮" &&
            t.Reason == "每日餐费" &&
            t.Card == "周期性账单" &&
            t.ImportFrom == "周期性账单自动生成"
        ));

        await _messageRepository.Received(1).AddAsync(Arg.Is<MessageRecord>(m =>
            m.Title == "周期性账单提醒" &&
            m.Content.Contains("支出") &&
            m.Content.Contains("100.00") &&
            m.Content.Contains("每日餐费") &&
            m.IsRead == false
        ));

        await _periodicRepository.Received(1).UpdateExecuteTimeAsync(
            Arg.Is(1L),
            Arg.Any<DateTime>(), 
            Arg.Any<DateTime?>()
        );
    }

    [Fact]
    public async Task ExecutePeriodicBillsAsync_每周账单()
    {
        // Arrange
        var periodicBill = new TransactionPeriodic
        {
            Id = 1,
            PeriodicType = PeriodicType.Daily, // Force execution to avoid DayOfWeek issues
            Amount = 200m,
            Type = TransactionType.Expense,
            Classify = "交通",
            Reason = "每周通勤费",
            IsEnabled = true,
            LastExecuteTime = DateTime.Today.AddDays(-7),
            NextExecuteTime = DateTime.Today
        };

        _periodicRepository.GetPendingPeriodicBillsAsync().Returns(new[] { periodicBill });
        _transactionRepository.AddAsync(Arg.Any<TransactionRecord>()).Returns(Task.FromResult(true));
        _messageRepository.AddAsync(Arg.Any<MessageRecord>()).Returns(Task.FromResult(true));
        _periodicRepository.UpdateExecuteTimeAsync(Arg.Any<long>(), Arg.Any<DateTime>(), Arg.Any<DateTime>())
            .Returns(Task.FromResult(true));

        // Act
        await _service.ExecutePeriodicBillsAsync();

        // Assert
        await _transactionRepository.Received(1).AddAsync(Arg.Is<TransactionRecord>(t =>
            t.Amount == 200m && // Positive matching input
            t.Type == TransactionType.Expense &&
            t.Classify == "交通" &&
            t.Reason == "每周通勤费"
        ));
    }

    [Fact]
    public async Task ExecutePeriodicBillsAsync_每月账单()
    {
        // Arrange
        var periodicBill = new TransactionPeriodic
        {
            Id = 1,
            PeriodicType = PeriodicType.Daily, // Force execution
            Amount = 5000m,
            Type = TransactionType.Income,
            Classify = "工资",
            Reason = "每月工资",
            IsEnabled = true,
            LastExecuteTime = DateTime.Today.AddMonths(-1),
            NextExecuteTime = DateTime.Today
        };

        _periodicRepository.GetPendingPeriodicBillsAsync().Returns(new[] { periodicBill });
        _transactionRepository.AddAsync(Arg.Any<TransactionRecord>()).Returns(Task.FromResult(true));
        _messageRepository.AddAsync(Arg.Any<MessageRecord>()).Returns(Task.FromResult(true));
        _periodicRepository.UpdateExecuteTimeAsync(Arg.Any<long>(), Arg.Any<DateTime>(), Arg.Any<DateTime>())
            .Returns(Task.FromResult(true));

        // Act
        await _service.ExecutePeriodicBillsAsync();

        // Assert
        await _transactionRepository.Received(1).AddAsync(Arg.Is<TransactionRecord>(t =>
            t.Amount == 5000m &&
            t.Type == TransactionType.Income &&
            t.Classify == "工资" &&
            t.Reason == "每月工资"
        ));

        await _messageRepository.Received(1).AddAsync(Arg.Is<MessageRecord>(m =>
            m.Content.Contains("收入") &&
            m.Content.Contains("5000.00") &&
            m.Content.Contains("每月工资")
        ));
    }
    
    [Fact]
    public async Task ExecutePeriodicBillsAsync_未达到执行时间()
    {
        // Arrange
        var periodicBill = new TransactionPeriodic
        {
            Id = 1,
            PeriodicType = PeriodicType.Weekly,
            PeriodicConfig = "1,3,5", 
            Amount = 200m,
            Type = TransactionType.Expense,
            Classify = "交通",
            Reason = "每周通勤费",
            IsEnabled = true,
            LastExecuteTime = DateTime.Today, // Executed today
            NextExecuteTime = DateTime.Today.AddDays(1)
        };

        _periodicRepository.GetPendingPeriodicBillsAsync().Returns(new[] { periodicBill });
        _transactionRepository.AddAsync(Arg.Any<TransactionRecord>()).Returns(Task.FromResult(true));

        // Act
        await _service.ExecutePeriodicBillsAsync();

        // Assert
        await _transactionRepository.DidNotReceive().AddAsync(Arg.Any<TransactionRecord>());
    }

    [Fact]
    public async Task ExecutePeriodicBillsAsync_今天已执行过()
    {
        // Arrange
        var periodicBill = new TransactionPeriodic
        {
            Id = 1,
            PeriodicType = PeriodicType.Daily,
            Amount = 100m,
            Type = TransactionType.Expense,
            Classify = "餐饮",
            Reason = "每日餐费",
            IsEnabled = true,
            LastExecuteTime = DateTime.Today, 
            NextExecuteTime = DateTime.Today.AddDays(1)
        };

        _periodicRepository.GetPendingPeriodicBillsAsync().Returns(new[] { periodicBill });
        _transactionRepository.AddAsync(Arg.Any<TransactionRecord>()).Returns(Task.FromResult(true));

        // Act
        await _service.ExecutePeriodicBillsAsync();

        // Assert
        await _transactionRepository.DidNotReceive().AddAsync(Arg.Any<TransactionRecord>());
    }

    [Fact]
    public async Task ExecutePeriodicBillsAsync_处理所有账单()
    {
        // Arrange
        var periodicBills = new[]
        {
            new TransactionPeriodic
            {
                Id = 1,
                PeriodicType = PeriodicType.Daily,
                Amount = 100m,
                Type = TransactionType.Expense,
                Classify = "餐饮",
                Reason = "每日餐费",
                IsEnabled = false, // Disabled
                LastExecuteTime = DateTime.Today.AddDays(-1),
                NextExecuteTime = DateTime.Today
            },
            new TransactionPeriodic
            {
                Id = 2,
                PeriodicType = PeriodicType.Daily,
                Amount = 200m,
                Type = TransactionType.Expense,
                Classify = "交通",
                Reason = "每日交通",
                IsEnabled = true,
                LastExecuteTime = DateTime.Today.AddDays(-1),
                NextExecuteTime = DateTime.Today
            }
        };

        _periodicRepository.GetPendingPeriodicBillsAsync().Returns(periodicBills);
        _transactionRepository.AddAsync(Arg.Any<TransactionRecord>()).Returns(Task.FromResult(true));
        _messageRepository.AddAsync(Arg.Any<MessageRecord>()).Returns(Task.FromResult(true));
        _periodicRepository.UpdateExecuteTimeAsync(Arg.Any<long>(), Arg.Any<DateTime>(), Arg.Any<DateTime>())
            .Returns(Task.FromResult(true));

        // Act
        await _service.ExecutePeriodicBillsAsync();

        // Assert
        // 只有启用的账单会被处理
        await _transactionRepository.Received(1).AddAsync(Arg.Is<TransactionRecord>(t => t.Reason == "每日交通"));
        await _transactionRepository.Received(0).AddAsync(Arg.Is<TransactionRecord>(t => t.Reason == "每日餐费"));
    }
}
