using Dapper;
using Lada.Framework.DTO;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Finance;
using Ladaer.BackEnd.Infrastructures.DTO.Results.Finance;
using System.Data;

namespace Ladaer.BackEnd.Infrastructures.Services.Report.Finance
{
    /// <summary>
    /// Interface cho service báo cáo thu chi
    /// </summary>
    public interface IIncomeExpenseReportService
    {
        /// <summary>
        /// Lấy báo cáo thu chi theo khoảng thời gian
        /// </summary>
        /// <param name="request">Thông tin khoảng thời gian</param>
        /// <param name="userId">ID người dùng</param>
        /// <returns>Báo cáo thu chi</returns>
        Task<IncomeExpenseReportResult> GetIncomeExpenseReportAsync(
            IncomeExpenseReportRequest request,
            int userId);
    }

    /// <summary>
    /// Service báo cáo thu chi
    /// </summary>
    public class IncomeExpenseReportService : IIncomeExpenseReportService
    {
        private readonly IDbConnection _dbConnection;

        /// <summary>
        /// Khởi tạo một thể hiện của <see cref="IncomeExpenseReportService"/>.
        /// </summary>
        /// <param name="dbConnection">Kết nối cơ sở dữ liệu.</param>
        public IncomeExpenseReportService(IDbConnection dbConnection)
        {
            _dbConnection = dbConnection;
        }

        /// <summary>
        /// Lấy báo cáo thu chi theo khoảng thời gian
        /// </summary>
        public async Task<IncomeExpenseReportResult> GetIncomeExpenseReportAsync(
            IncomeExpenseReportRequest request,
            int userId)
        {
            var result = new IncomeExpenseReportResult
            {
                FromDate = request.FromDate,
                ToDate = request.ToDate,
                Result = Result.Success
            };

            try
            {
                // Validate
                if (request.FromDate > request.ToDate)
                {
                    result.Result = Result.Failed;
                    result.Message = "Ngày bắt đầu phải nhỏ hơn hoặc bằng ngày kết thúc";
                    return result;
                }

                // Kiểm tra khoảng thời gian không quá 1 năm
                var daysDiff = (request.ToDate - request.FromDate).TotalDays;
                if (daysDiff > 365)
                {
                    result.Result = Result.Failed;
                    result.Message = "Khoảng thời gian không được vượt quá 1 năm";
                    return result;
                }

                var parameters = new DynamicParameters();
                parameters.Add("FromDate", request.FromDate);
                parameters.Add("ToDate", request.ToDate);

                // Gọi stored procedure
                var transactionData = (await _dbConnection.QueryAsync<TransactionReportRow>(
                    "sp_GetIncomeExpenseReport",
                    parameters,
                    commandType: CommandType.StoredProcedure
                )).ToList();

                // Generate date columns
                result.DateColumns = GenerateDateColumns(request.FromDate, request.ToDate);

                // Build report matrix
                result.Rows = BuildReportMatrix(transactionData, result.DateColumns);

                // Calculate totals
                CalculateTotals(result);

                result.Message = "Lấy báo cáo thành công";
            }
            catch (Exception ex)
            {
                result.Result = Result.SystemError;
                result.Message = $"Lỗi khi tạo báo cáo: {ex.Message}";
            }

            return result;
        }

        /// <summary>
        /// Tạo danh sách các cột ngày
        /// </summary>
        private List<string> GenerateDateColumns(DateTimeOffset from, DateTimeOffset to)
        {
            var dates = new List<string>();
            var current = from.Date;
            var end = to.Date;

            while (current <= end)
            {
                dates.Add(current.ToString("yyyy-MM-dd"));
                current = current.AddDays(1);
            }

            return dates;
        }

        /// <summary>
        /// Xây dựng ma trận dữ liệu báo cáo
        /// </summary>
        private List<IncomeExpenseReportRowItem> BuildReportMatrix(
            List<TransactionReportRow> data,
            List<string> dateColumns)
        {
            var rows = new List<IncomeExpenseReportRowItem>();

            // Nhóm theo Income và Outcome
            var incomeData = data.Where(d => d.TransactionDirection == "Income").ToList();
            var expenseData = data.Where(d => d.TransactionDirection == "Outcome").ToList();

            // Xây dựng phần THU
            if (incomeData.Any())
            {
                rows.AddRange(BuildIncomeRows(incomeData, dateColumns));
            }
            else
            {
                // Thêm dòng tổng thu = 0
                rows.Add(CreateEmptyTotalRow("Tổng thu", dateColumns, "Income"));
            }

            // Xây dựng phần CHI
            if (expenseData.Any())
            {
                rows.AddRange(BuildExpenseRows(expenseData, dateColumns));
            }
            else
            {
                // Thêm dòng tổng chi = 0
                rows.Add(CreateEmptyTotalRow("Tổng chi", dateColumns, "Expense"));
            }

            // Thêm dòng chênh lệch
            rows.Add(CreateNetAmountRow(rows, dateColumns));

            return rows;
        }

        /// <summary>
        /// Xây dựng các dòng THU
        /// </summary>
        private List<IncomeExpenseReportRowItem> BuildIncomeRows(
            List<TransactionReportRow> incomeData,
            List<string> dateColumns)
        {
            var rows = new List<IncomeExpenseReportRowItem>();

            // Group theo CategoryName
            var grouped = incomeData
                .GroupBy(d => new { d.CategoryName, d.Level })
                .OrderBy(g => g.Key.CategoryName);

            foreach (var group in grouped)
            {
                var row = new IncomeExpenseReportRowItem
                {
                    CategoryName = group.Key.CategoryName,
                    Level = group.Key.Level,
                    RowType = "Income",
                    DailyAmounts = new Dictionary<string, decimal>()
                };

                // Điền số liệu theo ngày
                foreach (var dateCol in dateColumns)
                {
                    var dateValue = DateTime.Parse(dateCol);
                    var amount = group
                        .Where(d => d.ReportDate.Date == dateValue)
                        .Sum(d => d.Amount);
                    row.DailyAmounts[dateCol] = amount;
                }

                row.Total = row.DailyAmounts.Values.Sum();
                rows.Add(row);
            }

            // Thêm dòng tổng thu
            rows.Add(CreateTotalRow("Tổng thu", rows, dateColumns, "Income"));

            return rows;
        }

        /// <summary>
        /// Xây dựng các dòng CHI
        /// </summary>
        private List<IncomeExpenseReportRowItem> BuildExpenseRows(
            List<TransactionReportRow> expenseData,
            List<string> dateColumns)
        {
            var rows = new List<IncomeExpenseReportRowItem>();

            // Tách ra: Đối soát và Chi phí vận hành
            var reconciliationData = expenseData.Where(d => d.CategoryName == "Chi phí đối soát").ToList();
            var operatingExpenseData = expenseData.Where(d => d.CategoryName != "Chi phí đối soát").ToList();

            // Xử lý đối soát
            if (reconciliationData.Any())
            {
                rows.Add(BuildSingleCategoryRow("Chi phí đối soát", reconciliationData, dateColumns, 0));
            }

            // Xử lý chi phí vận hành (có cấu trúc cây)
            if (operatingExpenseData.Any())
            {
                rows.AddRange(BuildExpenseTreeRows(operatingExpenseData, dateColumns));
            }

            // Thêm dòng tổng chi
            rows.Add(CreateTotalRow("Tổng chi", rows, dateColumns, "Expense"));

            return rows;
        }

        /// <summary>
        /// Xây dựng dòng cho một danh mục đơn
        /// </summary>
        private IncomeExpenseReportRowItem BuildSingleCategoryRow(
            string categoryName,
            List<TransactionReportRow> data,
            List<string> dateColumns,
            int level)
        {
            var row = new IncomeExpenseReportRowItem
            {
                CategoryName = categoryName,
                Level = level,
                RowType = "Expense",
                DailyAmounts = new Dictionary<string, decimal>()
            };

            // Điền số liệu theo ngày
            foreach (var dateCol in dateColumns)
            {
                var dateValue = DateTime.Parse(dateCol);
                var amount = data
                    .Where(d => d.ReportDate.Date == dateValue)
                    .Sum(d => d.Amount);
                row.DailyAmounts[dateCol] = amount;
            }

            row.Total = row.DailyAmounts.Values.Sum();
            return row;
        }

        /// <summary>
        /// Xây dựng cây chi phí vận hành
        /// </summary>
        private List<IncomeExpenseReportRowItem> BuildExpenseTreeRows(
            List<TransactionReportRow> data,
            List<string> dateColumns)
        {
            var rows = new List<IncomeExpenseReportRowItem>();

            // Lấy tất cả các category duy nhất
            var categories = data
                .Select(d => new
                {
                    d.CostCategoryId,
                    d.CategoryName,
                    d.ParentId,
                    d.Level,
                    d.DisplayOrder
                })
                .Distinct()
                .OrderBy(c => c.Level)
                .ThenBy(c => c.DisplayOrder)
                .ThenBy(c => c.CategoryName);

            foreach (var category in categories)
            {
                var categoryData = data.Where(d => d.CostCategoryId == category.CostCategoryId).ToList();

                var row = new IncomeExpenseReportRowItem
                {
                    CategoryId = category.CostCategoryId,
                    CategoryName = category.CategoryName,
                    ParentId = category.ParentId,
                    Level = category.Level,
                    RowType = "Expense",
                    DailyAmounts = new Dictionary<string, decimal>()
                };

                // Điền số liệu theo ngày
                foreach (var dateCol in dateColumns)
                {
                    var dateValue = DateTime.Parse(dateCol);
                    var amount = categoryData
                        .Where(d => d.ReportDate.Date == dateValue)
                        .Sum(d => d.Amount);
                    row.DailyAmounts[dateCol] = amount;
                }

                row.Total = row.DailyAmounts.Values.Sum();
                rows.Add(row);
            }

            return rows;
        }

        /// <summary>
        /// Tạo dòng tổng
        /// </summary>
        private IncomeExpenseReportRowItem CreateTotalRow(
            string name,
            List<IncomeExpenseReportRowItem> rows,
            List<string> dateColumns,
            string rowType)
        {
            var totalRow = new IncomeExpenseReportRowItem
            {
                CategoryName = name,
                Level = 0,
                RowType = "Total",
                DailyAmounts = new Dictionary<string, decimal>()
            };

            // Tổng hợp theo từng ngày
            foreach (var dateCol in dateColumns)
            {
                var dailyTotal = rows
                    .Where(r => r.RowType == rowType)
                    .Sum(r => r.DailyAmounts.ContainsKey(dateCol) ? r.DailyAmounts[dateCol] : 0);
                totalRow.DailyAmounts[dateCol] = dailyTotal;
            }

            totalRow.Total = totalRow.DailyAmounts.Values.Sum();
            return totalRow;
        }

        /// <summary>
        /// Tạo dòng tổng rỗng
        /// </summary>
        private IncomeExpenseReportRowItem CreateEmptyTotalRow(
            string name,
            List<string> dateColumns,
            string rowType)
        {
            var totalRow = new IncomeExpenseReportRowItem
            {
                CategoryName = name,
                Level = 0,
                RowType = "Total",
                DailyAmounts = new Dictionary<string, decimal>()
            };

            foreach (var dateCol in dateColumns)
            {
                totalRow.DailyAmounts[dateCol] = 0;
            }

            totalRow.Total = 0;
            return totalRow;
        }

        /// <summary>
        /// Tạo dòng chênh lệch (Thu - Chi)
        /// </summary>
        private IncomeExpenseReportRowItem CreateNetAmountRow(
            List<IncomeExpenseReportRowItem> allRows,
            List<string> dateColumns)
        {
            var netRow = new IncomeExpenseReportRowItem
            {
                CategoryName = "Chênh lệch (Thu - Chi)",
                Level = 0,
                RowType = "Net",
                DailyAmounts = new Dictionary<string, decimal>()
            };

            var incomeTotalRow = allRows.FirstOrDefault(r => r.CategoryName == "Tổng thu");
            var expenseTotalRow = allRows.FirstOrDefault(r => r.CategoryName == "Tổng chi");

            foreach (var dateCol in dateColumns)
            {
                var income = incomeTotalRow?.DailyAmounts.ContainsKey(dateCol) == true
                    ? incomeTotalRow.DailyAmounts[dateCol]
                    : 0;
                var expense = expenseTotalRow?.DailyAmounts.ContainsKey(dateCol) == true
                    ? expenseTotalRow.DailyAmounts[dateCol]
                    : 0;
                netRow.DailyAmounts[dateCol] = income - expense;
            }

            netRow.Total = netRow.DailyAmounts.Values.Sum();
            return netRow;
        }

        /// <summary>
        /// Tính tổng thu, tổng chi, chênh lệch
        /// </summary>
        private void CalculateTotals(IncomeExpenseReportResult result)
        {
            var incomeTotalRow = result.Rows.FirstOrDefault(r => r.CategoryName == "Tổng thu");
            var expenseTotalRow = result.Rows.FirstOrDefault(r => r.CategoryName == "Tổng chi");

            result.TotalIncome = incomeTotalRow?.Total ?? 0;
            result.TotalExpense = expenseTotalRow?.Total ?? 0;
            result.NetAmount = result.TotalIncome - result.TotalExpense;
        }
    }
}

