namespace Service;

/// <summary>
/// 周期性账单服务接口
/// </summary>
public interface ITransactionPeriodicService
{
    /// <summary>
    /// 执行周期性账单检查和生成
    /// </summary>
    Task ExecutePeriodicBillsAsync();

    /// <summary>
    /// 计算下次执行时间
    /// </summary>
    DateTime? CalculateNextExecuteTime(TransactionPeriodic periodic, DateTime baseTime);
}

/// <summary>
/// 周期性账单服务实现
/// </summary>
public class TransactionPeriodicService(
    ITransactionPeriodicRepository periodicRepository,
    ITransactionRecordRepository transactionRepository,
    IMessageRecordRepository messageRepository,
    ILogger<TransactionPeriodicService> logger
) : ITransactionPeriodicService
{
    public async Task ExecutePeriodicBillsAsync()
    {
        try
        {
            logger.LogInformation("开始执行周期性账单检查...");
        
            var pendingBills = await periodicRepository.GetPendingPeriodicBillsAsync();
            var billsList = pendingBills.ToList();
        
            logger.LogInformation("找到 {Count} 条需要执行的周期性账单", billsList.Count);

            foreach (var bill in billsList)
            {
                try
                {
                    // 检查是否满足执行条件
                    if (!ShouldExecuteToday(bill))
                    {
                        logger.LogInformation("周期性账单 {Id} 今天不需要执行", bill.Id);
                        continue;
                    }

                    // 创建交易记录
                    var transaction = new TransactionRecord
                    {
                        Amount = bill.Amount,
                        Type = bill.Type,
                        Classify = bill.Classify,
                        Reason = bill.Reason,
                        OccurredAt = DateTime.Now,
                        Card = "周期性账单",
                        ImportFrom = "周期性账单自动生成"
                    };

                    var success = await transactionRepository.AddAsync(transaction);
                
                    if (success)
                    {
                        logger.LogInformation("成功创建周期性账单交易记录: {Reason}, 金额: {Amount}", 
                            bill.Reason, bill.Amount);

                        // 创建未读消息
                        var message = new MessageRecord
                        {
                            Title = "周期性账单提醒",
                            Content = $"已自动生成{(bill.Type == TransactionType.Expense ? "支出" : "收入")}账单：{bill.Reason}，金额：{bill.Amount:F2}元",
                            IsRead = false
                        };
                        await messageRepository.AddAsync(message);

                        // 更新执行时间
                        var now = DateTime.Now;
                        var nextTime = CalculateNextExecuteTime(bill, now);
                        await periodicRepository.UpdateExecuteTimeAsync(bill.Id, now, nextTime);
                    
                        logger.LogInformation("周期性账单 {Id} 下次执行时间: {NextTime}", 
                            bill.Id, nextTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "无");
                    }
                    else
                    {
                        logger.LogWarning("创建周期性账单交易记录失败: {BillId}", bill.Id);
                    }
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, "处理周期性账单 {BillId} 时出错", bill.Id);
                }
            }

            logger.LogInformation("周期性账单检查执行完成");
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "执行周期性账单检查时发生错误");
        }
    }

    /// <summary>
    /// 判断今天是否需要执行
    /// </summary>
    private bool ShouldExecuteToday(TransactionPeriodic bill)
    {
        var today = DateTime.Today;
    
        // 如果从未执行过，需要执行
        if (bill.LastExecuteTime == null)
        {
            return true;
        }

        // 如果今天已经执行过，不需要再执行
        if (bill.LastExecuteTime.Value.Date == today)
        {
            return false;
        }

        return bill.PeriodicType switch
        {
            PeriodicType.Daily => true, // 每天都执行
            PeriodicType.Weekly => ShouldExecuteWeekly(bill.PeriodicConfig, today),
            PeriodicType.Monthly => ShouldExecuteMonthly(bill.PeriodicConfig, today),
            PeriodicType.Quarterly => ShouldExecuteQuarterly(bill.PeriodicConfig, today),
            PeriodicType.Yearly => ShouldExecuteYearly(bill.PeriodicConfig, today),
            _ => false
        };
    }

    /// <summary>
    /// 判断是否需要在本周执行
    /// </summary>
    private bool ShouldExecuteWeekly(string config, DateTime today)
    {
        if (string.IsNullOrWhiteSpace(config))
            return false;

        var dayOfWeek = (int)today.DayOfWeek; // 0=Sunday, 1=Monday, ..., 6=Saturday
        var executeDays = config.Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(d => int.TryParse(d.Trim(), out var day) ? day : -1)
            .Where(d => d >= 0 && d <= 6)
            .ToList();

        return executeDays.Contains(dayOfWeek);
    }

    /// <summary>
    /// 判断是否需要在本月执行
    /// </summary>
    private bool ShouldExecuteMonthly(string config, DateTime today)
    {
        if (string.IsNullOrWhiteSpace(config))
            return false;

        var executeDays = config.Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(d => int.TryParse(d.Trim(), out var day) ? day : -1)
            .Where(d => d >= 1 && d <= 31)
            .ToList();

        // 如果当前为月末，且配置中有大于当月天数的日期，则也执行
        var daysInMonth = DateTime.DaysInMonth(today.Year, today.Month);
        if (today.Day == daysInMonth && executeDays.Any(d => d > daysInMonth))
        {
            return true;
        }

        return executeDays.Contains(today.Day);
    }

    /// <summary>
    /// 判断是否需要在本季度执行
    /// </summary>
    private bool ShouldExecuteQuarterly(string config, DateTime today)
    {
        if (string.IsNullOrWhiteSpace(config) || !int.TryParse(config, out var dayOfQuarter))
            return false;

        // 计算当前是本季度的第几天
        var quarterStartMonth = (today.Month - 1) / 3 * 3 + 1;
        var quarterStart = new DateTime(today.Year, quarterStartMonth, 1);
        var daysSinceQuarterStart = (today - quarterStart).Days + 1;

        return daysSinceQuarterStart == dayOfQuarter;
    }

    /// <summary>
    /// 判断是否需要在本年执行
    /// </summary>
    private bool ShouldExecuteYearly(string config, DateTime today)
    {
        if (string.IsNullOrWhiteSpace(config) || !int.TryParse(config, out var dayOfYear))
            return false;

        return today.DayOfYear == dayOfYear;
    }

    /// <summary>
    /// 计算下次执行时间
    /// </summary>
    public DateTime? CalculateNextExecuteTime(TransactionPeriodic periodic, DateTime baseTime)
    {
        return periodic.PeriodicType switch
        {
            PeriodicType.Daily => baseTime.Date.AddDays(1),
            PeriodicType.Weekly => CalculateNextWeekly(periodic.PeriodicConfig, baseTime),
            PeriodicType.Monthly => CalculateNextMonthly(periodic.PeriodicConfig, baseTime),
            PeriodicType.Quarterly => CalculateNextQuarterly(periodic.PeriodicConfig, baseTime),
            PeriodicType.Yearly => CalculateNextYearly(periodic.PeriodicConfig, baseTime),
            _ => null
        };
    }

    private DateTime? CalculateNextWeekly(string config, DateTime baseTime)
    {
        if (string.IsNullOrWhiteSpace(config))
            return null;

        var executeDays = config.Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(d => int.TryParse(d.Trim(), out var day) ? day : -1)
            .Where(d => d >= 0 && d <= 6)
            .OrderBy(d => d)
            .ToList();

        if (!executeDays.Any())
            return null;

        var currentDayOfWeek = (int)baseTime.DayOfWeek;
    
        // 找下一个执行日
        var nextDay = executeDays.FirstOrDefault(d => d > currentDayOfWeek);
        if (nextDay > 0)
        {
            var daysToAdd = nextDay - currentDayOfWeek;
            return baseTime.Date.AddDays(daysToAdd);
        }
    
        // 下周的第一个执行日
        var firstDay = executeDays.First();
        var daysUntilNextWeek = 7 - currentDayOfWeek + firstDay;
        return baseTime.Date.AddDays(daysUntilNextWeek);
    }

    private DateTime? CalculateNextMonthly(string config, DateTime baseTime)
    {
        if (string.IsNullOrWhiteSpace(config))
            return null;

        var executeDays = config.Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(d => int.TryParse(d.Trim(), out var day) ? day : -1)
            .Where(d => d >= 1 && d <= 31)
            .OrderBy(d => d)
            .ToList();

        if (!executeDays.Any())
            return null;

        // 找本月的下一个执行日
        var nextDay = executeDays.FirstOrDefault(d => d > baseTime.Day);
        if (nextDay > 0)
        {
            var daysInMonth = DateTime.DaysInMonth(baseTime.Year, baseTime.Month);
            if (nextDay <= daysInMonth)
            {
                return new DateTime(baseTime.Year, baseTime.Month, nextDay);
            }
        }

        // 下个月的第一个执行日
        var nextMonth = baseTime.AddMonths(1);
        var firstDay = executeDays.First();
        var daysInNextMonth = DateTime.DaysInMonth(nextMonth.Year, nextMonth.Month);
        var actualDay = Math.Min(firstDay, daysInNextMonth);
        return new DateTime(nextMonth.Year, nextMonth.Month, actualDay);
    }

    private DateTime? CalculateNextQuarterly(string config, DateTime baseTime)
    {
        if (string.IsNullOrWhiteSpace(config) || !int.TryParse(config, out var dayOfQuarter))
            return null;

        // 计算下一个季度的开始时间
        var currentQuarterStartMonth = ((baseTime.Month - 1) / 3) * 3 + 1;
        var nextQuarterStartMonth = currentQuarterStartMonth + 3;
        var nextQuarterYear = baseTime.Year;
    
        if (nextQuarterStartMonth > 12)
        {
            nextQuarterStartMonth = 1;
            nextQuarterYear++;
        }

        var nextQuarterStart = new DateTime(nextQuarterYear, nextQuarterStartMonth, 1);
        return nextQuarterStart.AddDays(dayOfQuarter - 1);
    }

    private DateTime? CalculateNextYearly(string config, DateTime baseTime)
    {
        if (string.IsNullOrWhiteSpace(config) || !int.TryParse(config, out var dayOfYear))
            return null;

        var nextYear = baseTime.Year;
        if (baseTime.DayOfYear >= dayOfYear)
        {
            nextYear++;
        }

        // 处理闰年情况
        var daysInYear = DateTime.IsLeapYear(nextYear) ? 366 : 365;
        var actualDay = Math.Min(dayOfYear, daysInYear);
    
        return new DateTime(nextYear, 1, 1).AddDays(actualDay - 1);
    }
}
