using Service.AI;
using Service.Message;
using Service.Transaction;

namespace Service.Budget;

public interface IBudgetService
{
    Task<List<BudgetResult>> GetListAsync(DateTime referenceDate);

    Task<string> ArchiveBudgetsAsync(int year, int month);

    /// <summary>
    /// 获取指定分类的统计信息（月度和年度）
    /// </summary>
    Task<BudgetCategoryStats> GetCategoryStatsAsync(BudgetCategory category, DateTime referenceDate);

    /// <summary>
    /// 获取未被预算覆盖的分类统计信息
    /// </summary>
    Task<List<UncoveredCategoryDetail>> GetUncoveredCategoriesAsync(BudgetCategory category, DateTime? referenceDate = null);

    Task<string?> GetArchiveSummaryAsync(int year, int month);

    /// <summary>
    /// 获取指定周期的存款预算信息
    /// </summary>
    Task<BudgetResult?> GetSavingsBudgetAsync(int year, int month, BudgetPeriodType type);
}

[UsedImplicitly]
public class BudgetService(
    IBudgetRepository budgetRepository,
    IBudgetArchiveRepository budgetArchiveRepository,
    ITransactionRecordRepository transactionRecordRepository,
    ITransactionStatisticsService transactionStatisticsService,
    IOpenAiService openAiService,
    IMessageService messageService,
    ILogger<BudgetService> logger,
    IBudgetSavingsService budgetSavingsService,
    IDateTimeProvider dateTimeProvider,
    IBudgetStatsService budgetStatsService
) : IBudgetService
{
    public async Task<List<BudgetResult>> GetListAsync(DateTime referenceDate)
    {
        var year = referenceDate.Year;
        var month = referenceDate.Month;

        var isArchive = year < dateTimeProvider.Now.Year
                     || (year == dateTimeProvider.Now.Year && month < dateTimeProvider.Now.Month);

        if (isArchive)
        {
            var archive = await budgetArchiveRepository.GetArchiveAsync(year, month);

            if (archive != null)
            {
                var (start, end) = GetPeriodRange(dateTimeProvider.Now, BudgetPeriodType.Month, referenceDate);
                return [.. archive.Content.Select(c => new BudgetResult
                {
                    Id = c.Id,
                    Name = c.Name,
                    Type = c.Type,
                    Limit = c.Limit,
                    Current = c.Actual,
                    Category = c.Category,
                    SelectedCategories = c.SelectedCategories,
                    NoLimit = c.NoLimit,
                    IsMandatoryExpense = c.IsMandatoryExpense,
                    Description = c.Description,
                    PeriodStart = start,
                    PeriodEnd = end,
                })];
            }

            logger.LogWarning("获取预算列表时发现归档数据缺失，Year: {Year}, Month: {Month}", year, month);
        }

        var budgets = await budgetRepository.GetAllAsync();
        var dtos = new List<BudgetResult?>();

        foreach (var budget in budgets)
        {
            var currentAmount = await CalculateCurrentAmountAsync(budget, referenceDate);
            dtos.Add(BudgetResult.FromEntity(budget, currentAmount, referenceDate));
        }

        // 创造虚拟的存款预算
        dtos.Add(await budgetSavingsService.GetSavingsDtoAsync(
            BudgetPeriodType.Month,
            referenceDate,
            budgets));
        dtos.Add(await budgetSavingsService.GetSavingsDtoAsync(
            BudgetPeriodType.Year,
            referenceDate,
            budgets));

        dtos = dtos
            .Where(x => x != null)
            .Cast<BudgetResult>()
            .OrderByDescending(x => x.IsMandatoryExpense)
            .ThenBy(x => x.Type)
            .ThenByDescending(x => x.Current)
            .ToList()!;

        return [.. dtos.Where(dto => dto != null).Cast<BudgetResult>()];
    }

    public async Task<BudgetResult?> GetSavingsBudgetAsync(int year, int month, BudgetPeriodType type)
    {
        var referenceDate = new DateTime(year, month, 1);
        return await budgetSavingsService.GetSavingsDtoAsync(type, referenceDate);
    }

    public async Task<BudgetCategoryStats> GetCategoryStatsAsync(BudgetCategory category, DateTime referenceDate)
    {
        return await budgetStatsService.GetCategoryStatsAsync(category, referenceDate);
    }

    public async Task<List<UncoveredCategoryDetail>> GetUncoveredCategoriesAsync(BudgetCategory category, DateTime? referenceDate = null)
    {
        var date = referenceDate ?? dateTimeProvider.Now;
        var transactionType = category switch
        {
            BudgetCategory.Expense => TransactionType.Expense,
            BudgetCategory.Income => TransactionType.Income,
            _ => TransactionType.None
        };

        if (transactionType == TransactionType.None) return [];

        // 1. 获取所有预算
        var budgets = (await budgetRepository.GetAllAsync()).ToList();
        var coveredCategories = budgets
            .Where(b => b.Category == category)
            .SelectMany(b => b.SelectedCategories.Split(',', StringSplitOptions.RemoveEmptyEntries))
            .ToHashSet();

        // 2. 获取分类统计
        var stats = await transactionStatisticsService.GetCategoryStatisticsAsync(date.Year, date.Month, transactionType);

        // 3. 过滤未覆盖的
        return stats
            .Where(s => !coveredCategories.Contains(s.Classify))
            .Select(s => new UncoveredCategoryDetail
            {
                Category = s.Classify,
                TransactionCount = s.Count,
                TotalAmount = s.Amount
            })
            .OrderByDescending(x => x.TotalAmount)
            .ToList();
    }

    public async Task<string?> GetArchiveSummaryAsync(int year, int month)
    {
        var archive = await budgetArchiveRepository.GetArchiveAsync(year, month);
        return archive?.Summary;
    }



    public async Task<string> ArchiveBudgetsAsync(int year, int month)
    {
        var referenceDate = new DateTime(year, month, 1);

        var budgets = await GetListAsync(referenceDate);

        var expenseSurplus = budgets
            .Where(b => b.Category == BudgetCategory.Expense && !b.NoLimit && b.Type == BudgetPeriodType.Month)
            .Sum(b => b.Limit - b.Current);

        var incomeSurplus = budgets
            .Where(b => b.Category == BudgetCategory.Income && !b.NoLimit && b.Type == BudgetPeriodType.Month)
            .Sum(b => b.Current - b.Limit);

        var content = budgets.Select(b => new BudgetArchiveContent
        {
            Id = b.Id,
            Name = b.Name,
            Type = b.Type,
            Limit = b.Limit,
            Actual = b.Current,
            Category = b.Category,
            SelectedCategories = b.SelectedCategories,
            NoLimit = b.NoLimit,
            IsMandatoryExpense = b.IsMandatoryExpense,
            Description = b.Description
        }).ToArray();

        var archive = await budgetArchiveRepository.GetArchiveAsync(year, month);

        if (archive != null)
        {
            archive.Content = content;
            archive.ArchiveDate = dateTimeProvider.Now;
            archive.ExpenseSurplus = expenseSurplus;
            archive.IncomeSurplus = incomeSurplus;
            if (!await budgetArchiveRepository.UpdateAsync(archive))
            {
                return "更新预算归档失败";
            }
        }
        else
        {
            archive = new BudgetArchive
            {
                Year = year,
                Month = month,
                Content = content,
                ArchiveDate = dateTimeProvider.Now,
                ExpenseSurplus = expenseSurplus,
                IncomeSurplus = incomeSurplus
            };

            if (!await budgetArchiveRepository.AddAsync(archive))
            {
                return "保存预算归档失败";
            }
        }

        _ = NotifyAsync(year, month);

        return string.Empty;
    }

    private async Task NotifyAsync(int year, int month)
    {
        try
        {
            var archives = await budgetArchiveRepository.GetListAsync(year, month);

            var archiveData = archives.SelectMany(a => a.Content.Select(c => new
            {
                c.Name,
                Type = c.Type.ToString(),
                c.Limit,
                c.Actual,
                Category = c.Category.ToString(),
                c.SelectedCategories
            })).ToList();

            var yearTransactions = await transactionRecordRepository.ExecuteDynamicSqlAsync(
                $"""
                SELECT 
                  COUNT(*) AS TransactionCount, 
                  SUM(ABS(Amount)) AS TotalAmount, 
                  Type, 
                  Classify
                FROM TransactionRecord
                WHERE OccurredAt >= '{year}-01-01' 
                  AND OccurredAt < '{year + 1}-01-01'
                GROUP BY Type, Classify
                ORDER BY TotalAmount DESC
                """
            );
            var monthYear = new DateTime(year, month, 1).AddMonths(1);
            var monthTransactions = await transactionRecordRepository.ExecuteDynamicSqlAsync(
                $"""
                SELECT 
                  COUNT(*) AS TransactionCount, 
                  SUM(ABS(Amount)) AS TotalAmount, 
                  Type, 
                  Classify
                FROM TransactionRecord
                WHERE OccurredAt >= '{year}-{month:00}-01' 
                  AND OccurredAt < '{monthYear:yyyy-MM-dd}'
                GROUP BY Type, Classify
                ORDER BY TotalAmount DESC
                """
            );

            // 分析未被预算覆盖的分类 (仅针对支出类型 Type=0)
            var budgetedCategories = archiveData
                .SelectMany(b => b.SelectedCategories)
                .Where(c => !string.IsNullOrEmpty(c))
                .Distinct()
                .ToHashSet();

            var uncovered = monthTransactions
                .Where(t =>
                {
                    var dict = (IDictionary<string, object>)t;
                    var classify = dict["Classify"].ToString() ?? "";
                    var type = Convert.ToInt32(dict["Type"]);
                    return type == 0 && !budgetedCategories.Contains(classify);
                })
                .ToList();

            logger.LogInformation("预算执行数据{JSON}", JsonSerializer.Serialize(archiveData));
            logger.LogInformation("本月消费明细{JSON}", JsonSerializer.Serialize(monthTransactions));
            logger.LogInformation("全年累计消费概况{JSON}", JsonSerializer.Serialize(yearTransactions));
            logger.LogInformation("未被预算覆盖的分类{JSON}", JsonSerializer.Serialize(uncovered));

            var dataPrompt = $"""
            报告周期：{year}年{month}月

            1. 预算执行数据（JSON）：
            {JsonSerializer.Serialize(archiveData)}
            
            2. 本月账单类目明细（按分类, JSON）：
            {JsonSerializer.Serialize(monthTransactions)}
            
            3. 全年累计账单类目明细（按分类, JSON）：
            {JsonSerializer.Serialize(yearTransactions)}

            4. 未被任何预算覆盖的支出分类（JSON）：
            {JsonSerializer.Serialize(uncovered)}

            请生成一份专业且美观的预算执行分析报告，严格遵守以下要求：
            
            【内容要求】
            1. 概览：总结本月预算达成情况。
            2. 预算详情：使用 HTML 表格展示预算执行明细（预算项、预算额、实际额、使用/达成率、状态）。
            3. 超支/异常预警：重点分析超支项或支出异常的分类。
            4. 消费透视：针对“未被预算覆盖的支出”提供分析建议。分析这些账单产生的合理性，并评估是否需要为其中的大额或频发分类建立新预算。
            5. 改进建议：根据当前时间进度和预算完成进度，基于本月整体收入支出情况，给出下月预算调整或消费改进的专业化建议。
            6. 语言风格：专业、清晰、简洁，适合财务报告阅读。
            7. 如果报告月份是12月，需要报告年度预算的执行情况。
            
            【格式要求】
            1. 使用HTML格式（移动端H5页面风格）
            2. 生成清晰的报告标题（基于用户问题）
            3. 使用表格展示统计数据（table > thead/tbody > tr > th/td），
                3.1 table要求不能超过屏幕宽度，尽可能简洁明了，避免冗余信息
                3.2 预算金额精确到整数即可，实际金额精确到小数点后1位
            4. 使用合适的HTML标签：h2（标题）、h3（小节）、p（段落）、table（表格）、ul/li（列表）、strong（强调）
            5. 支出金额用 <span class='expense-value'>金额</span> 包裹
            6. 收入金额用 <span class='income-value'>金额</span> 包裹
            7. 重要结论用 <span class='highlight'>内容</span> 高亮
            
            【样式限制（重要）】
            8. 不要包含 html、body、head 标签
            9. 不要使用任何 style 属性或 <style> 标签
            10. 不要设置 background、background-color、color 等样式属性
            11. 不要使用 div 包裹大段内容

            【系统信息】
            当前时间：{dateTimeProvider.Now:yyyy-MM-dd HH:mm:ss}
            预算归档周期：{year}年{month}月
            
            直接输出纯净 HTML 内容，不要带有 Markdown 代码块包裹。
            """;

            var htmlReport = await openAiService.ChatAsync(dataPrompt);
            if (!string.IsNullOrEmpty(htmlReport))
            {
                await messageService.AddAsync(
                    title: $"{year}年{month}月 - 预算归档报告",
                    content: htmlReport,
                    type: MessageType.Html,
                    url: "/balance?tab=message");

                // 同时保存到归档总结
                var first = archives.First();
                first.Summary = htmlReport;
                await budgetArchiveRepository.UpdateAsync(first);
            }
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "生成预算执行通知报告失败");
        }
    }

    private async Task<decimal> CalculateCurrentAmountAsync(BudgetRecord budget, DateTime? now = null)
    {
        var referenceDate = now ?? dateTimeProvider.Now;
        var (startDate, endDate) = GetPeriodRange(budget.StartDate, budget.Type, referenceDate);

        var actualAmount = await budgetRepository.GetCurrentAmountAsync(budget, startDate, endDate);

        // 如果是硬性消费，且是当前年当前月，则根据经过的天数累加
        if (actualAmount == 0
            && budget.IsMandatoryExpense
            && referenceDate.Year == startDate.Year
            && (budget.Type == BudgetPeriodType.Year || referenceDate.Month == startDate.Month))
        {
            if (budget.Type == BudgetPeriodType.Month)
            {
                // 计算本月的天数
                var daysInMonth = DateTime.DaysInMonth(referenceDate.Year, referenceDate.Month);
                // 计算当前已经过的天数（包括今天）
                var daysElapsed = referenceDate.Day;
                // 根据预算金额和经过天数计算应累加的金额
                var mandatoryAccumulation = budget.Limit * daysElapsed / daysInMonth;
                // 返回实际消费和硬性消费累加中的较大值
                return mandatoryAccumulation;
            }

            if (budget.Type == BudgetPeriodType.Year)
            {
                // 计算本年的天数（考虑闰年）
                var daysInYear = DateTime.IsLeapYear(referenceDate.Year) ? 366 : 365;
                // 计算当前已经过的天数（包括今天）
                var daysElapsed = referenceDate.DayOfYear;
                // 根据预算金额和经过天数计算应累加的金额
                var mandatoryAccumulation = budget.Limit * daysElapsed / daysInYear;
                // 返回实际消费和硬性消费累加中的较大值
                return mandatoryAccumulation;
            }

        }

        return actualAmount;
    }

    internal static (DateTime start, DateTime end) GetPeriodRange(DateTime startDate, BudgetPeriodType type, DateTime referenceDate)
    {
        DateTime start;
        DateTime end;

        if (type == BudgetPeriodType.Month)
        {
            start = new DateTime(referenceDate.Year, referenceDate.Month, 1);
            end = start.AddMonths(1).AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
        }
        else if (type == BudgetPeriodType.Year)
        {
            start = new DateTime(referenceDate.Year, 1, 1);
            end = new DateTime(referenceDate.Year, 12, 31).AddHours(23).AddMinutes(59).AddSeconds(59);
        }
        else
        {
            start = startDate;
            end = DateTime.MaxValue;
        }

        return (start, end);
    }
}

public record BudgetResult
{
    public long Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public BudgetPeriodType Type { get; set; }
    public decimal Limit { get; set; }
    public decimal Current { get; set; }
    public BudgetCategory Category { get; set; }
    public string[] SelectedCategories { get; set; } = [];
    public string StartDate { get; set; } = string.Empty;
    public string Period { get; set; } = string.Empty;
    public DateTime? PeriodStart { get; set; }
    public DateTime? PeriodEnd { get; set; }
    public bool NoLimit { get; set; }
    public bool IsMandatoryExpense { get; set; }
    public string Description { get; set; } = string.Empty;

    public static BudgetResult FromEntity(
        BudgetRecord entity,
        decimal currentAmount,
        DateTime referenceDate,
        string description = "")
    {
        var date = referenceDate;
        var (start, end) = BudgetService.GetPeriodRange(entity.StartDate, entity.Type, date);

        return new BudgetResult
        {
            Id = entity.Id,
            Name = entity.Name,
            Type = entity.Type,
            Limit = entity.Limit,
            Current = currentAmount,
            Category = entity.Category,
            SelectedCategories = string.IsNullOrEmpty(entity.SelectedCategories)
                ? []
                : entity.SelectedCategories.Split(','),
            StartDate = entity.StartDate.ToString("yyyy-MM-dd"),
            Period = entity.Type switch
            {
                BudgetPeriodType.Year => $"{start:yy}年",
                BudgetPeriodType.Month => $"{start:yy}年第{start.Month}月",
                _ => $"{start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}"
            },
            PeriodStart = start,
            PeriodEnd = end,
            NoLimit = entity.NoLimit,
            IsMandatoryExpense = entity.IsMandatoryExpense,
            Description = description
        };
    }
}
/// <summary>
/// 预算统计结果 DTO
/// </summary>
public class BudgetStatsDto
{
    /// <summary>
    /// 统计周期类型（Month/Year）
    /// </summary>
    public BudgetPeriodType PeriodType { get; set; }

    /// <summary>
    /// 使用率百分比（0-100）
    /// </summary>
    public decimal Rate { get; set; }

    /// <summary>
    /// 实际金额
    /// </summary>
    public decimal Current { get; set; }

    /// <summary>
    /// 目标/限额金额
    /// </summary>
    public decimal Limit { get; set; }

    /// <summary>
    /// 预算项数量
    /// </summary>
    public int Count { get; set; }

    /// <summary>
    /// 每日/每月累计金额趋势（对应当前周期内的实际发生额累计值）
    /// </summary>
    public List<decimal?> Trend { get; set; } = [];

    /// <summary>
    /// HTML 格式的详细描述（罗列每个预算的额度和实际值及计算公式）
    /// </summary>
    public string Description { get; set; } = string.Empty;
}

/// <summary>
/// 分类统计结果
/// </summary>
public class BudgetCategoryStats
{
    /// <summary>
    /// 月度统计
    /// </summary>
    public BudgetStatsDto Month { get; set; } = new();

    /// <summary>
    /// 年度统计
    /// </summary>
    public BudgetStatsDto Year { get; set; } = new();
}

public class UncoveredCategoryDetail
{
    public string Category { get; set; } = string.Empty;
    public int TransactionCount { get; set; }
    public decimal TotalAmount { get; set; }
}
