using Dapper;
using Lada.Framework.DTO;
using Lada.Framework.DTO.SmartTable;
using Ladaer.BackEnd.Infrastructures.DTO.Items.Finances;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Finances;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Orders.Shared;
using Newtonsoft.Json;
using System.Data;
using Lada.Framework.Data.Domains.Finance.Wallets;
using Lada.Framework.Data.Repositories;
using Lada.Framework.Data.Enums;
using Lada.Framework.Data.Domains;
using Microsoft.EntityFrameworkCore;
using Lada.Framework.Infrastructures.Helpers;
using Lada.Framework.Data.Domains.Orders;
using static Lada.Framework.Data.Enums.OrderFinEnum;
using Ladaer.BackEnd.Infrastructures.Services.Identity;
using Ladaer.BackEnd.Infrastructures.Services.Orders;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Orders;
using Ladaer.BackEnd.Infrastructures.DTO;

namespace Ladaer.BackEnd.Infrastructures.Services.Finances
{
    public interface ICashierService
    {
        Task<SmartTableResult<CashierBalanceItem>> Search(SmartTableParam param, int userId);
        Task<BaseResult> CollectCashier(CollectCashierModel model);
    }

    public class CashierService : ICashierService
    {
        private readonly IRepository<OrderFin> _orderFinRepository;
        private readonly IRepository<Wallet> _walletRepository;
        private readonly IRepository<Transaction> _transactionRepository;
        private readonly IRepository<Order> _orderRepository;
        private readonly IDbConnection _connection;
        private readonly IUserService _userService;
        private readonly IOrderTrackService _orderTrackService;
        private readonly IOrderTimeTrackService _orderTimeTrackService;

        public CashierService(IRepository<Wallet> walletRepository,
            IRepository<Transaction> transactionRepository,
            IDbConnection connection,
            IRepository<OrderFin> orderFinRepository,
            IRepository<Order> orderRepository,
            IUserService userService,
            IOrderTrackService orderTrackService,
            IOrderTimeTrackService orderTimeTrackService)
        {
            _walletRepository = walletRepository;
            _transactionRepository = transactionRepository;
            _connection = connection;
            _orderFinRepository = orderFinRepository;
            _orderRepository = orderRepository;
            _userService = userService;
            _orderTrackService = orderTrackService;
            _orderTimeTrackService = orderTimeTrackService;
        }

        public async Task<SmartTableResult<CashierBalanceItem>> Search(SmartTableParam param, int userId)
        {
            var result = new SmartTableResult<CashierBalanceItem>()
            {
                Items = new List<CashierBalanceItem>(),
                TotalRecord = 0
            };
            if (param.Search.PredicateObject != null)
            {
                var parameters = new DynamicParameters();

                dynamic search = param.Search.PredicateObject;
                if (search.Keyword != null)
                {
                    string keyword = search.Keyword;
                    keyword = keyword.Trim().ToLower();
                    parameters.Add("@Keyword", keyword);
                }

                if (param.Pagination.PageIndex <= 0)
                {
                    param.Pagination.PageIndex = 1;
                }
                if (param.Pagination.PageSize <= 0)
                {
                    param.Pagination.PageSize = 15;
                }
                parameters.Add("@PageNumber", param.Pagination.PageIndex);
                parameters.Add("@PageSize", param.Pagination.PageSize);

                var orderFeeStageId = (int)(await GetFeeStageByUserId(userId));
                var orderCodStageId = (int)await GetCodStageByUserId(userId);
                parameters.Add("@DestFeeCurrentStage", orderFeeStageId);
                parameters.Add("@SourceFeeCurrentStage", orderFeeStageId);
                parameters.Add("@CodCurrentStage", orderCodStageId);

                var multi = await _connection.QueryMultipleAsync(
                    "[dbo].[sp_GetCashierBalance]",
                    parameters,
                    commandType: CommandType.StoredProcedure);

                var totalRecord = await multi.ReadFirstOrDefaultAsync<int>();
                var cashierBalances = (await multi.ReadAsync<CashierBalanceItem>()).ToList();
                var cashierBalanceItems = new List<CashierBalanceItem>();
                foreach (var item in cashierBalances)
                {
                    try
                    {
                        if (!string.IsNullOrEmpty(item.Orders))
                        {
                            item.OrderList = JsonConvert.DeserializeObject<List<CashierBalanceOrderItem>>(item.Orders) ?? new List<CashierBalanceOrderItem>();
                        }
                        else
                        {
                            item.OrderList = new List<CashierBalanceOrderItem>();
                        }
                        cashierBalanceItems.Add(item);
                    }
                    catch (JsonException)
                    {
                        item.OrderList = new List<CashierBalanceOrderItem>();
                    }


                }

                result.Items = cashierBalanceItems;
                result.TotalRecord = totalRecord;


            }

            return result;
        }

        public async Task<BaseResult> CollectCashier(CollectCashierModel model)
        {
            var result = new BaseResult();
            var currentTime = DateTimeHelper.CurrentTime();
            var orderFeeStage = await GetFeeStageByUserId(model.UpdatedBy);
            var orderCodStage = await GetCodStageByUserId(model.UpdatedBy);

            using (var dbtransaction = _walletRepository.BeginTransaction())
            {
                try
                {
                    var wallet = new Wallet();
                    var existingWallet = await _walletRepository.Query()
                        .FirstOrDefaultAsync(w => w.UserId == model.UpdatedBy && w.WalletType == WalletType.Personal);

                    if (existingWallet != null)
                    {
                        wallet = existingWallet;
                    }
                    else
                    {
                        wallet = new Wallet
                        {
                            UserId = model.UpdatedBy,
                            Balance = 0,
                            WalletType = WalletType.Personal,
                            WalletStatus = WalletStatus.Active

                        };
                        wallet = wallet.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

                        await _walletRepository.InsertAsync(wallet);

                    }
                    var transaction = new Transaction
                    {
                        Description = $"Nhận tiền từ lái xe",
                        WalletId = wallet.Id,
                        Amount = model.TotalAmount,
                        CashAmount = model.CashAmount,
                        TransferAmount = model.TransferAmount,
                        Note = model.Note,
                        FromUserId = model.FromUserId,
                        TransactionType = TransactionType.CollectFromDriver,
                        TransactionDate = currentTime,
                        TransactionDirection = TransactionDirection.Income
                    };
                    transaction = transaction.UpdateCommonGuid(model.UpdatedBy, model.UpdatedByUserName);

                    var orderIds = model.OrderList.Select(o => o.Id).ToList();
                    var orderFins = _orderFinRepository.Query().Where(o => orderIds.Contains(o.Id)).ToList();


                    foreach (var order in model.OrderList)
                    {
                        var transactionOrder = new TransactionOrder
                        {
                            OrderId = order.Id,
                            CodAmount = order.CodAmount,
                            SourceFeeAmount = order.SourceFee,
                            DestFeeAmount = order.DestFee,
                            CreatedTime = currentTime,
                            UpdatedTime = currentTime
                        };

                        var orderFin = orderFins.FirstOrDefault(o => o.Id == order.Id);
                        if (orderFin != null && orderFin.Id > 0)
                        {
                            var needUpdate = false;
                            if (order.CodAmount > 0)
                            {
                                orderFin.CodCashierId = model.UpdatedBy;
                                orderFin.CodCashierUsername = model.UpdatedByUserName;
                                orderFin.CodStage = orderCodStage;
                                if (orderFin.PayFeeType == PayFeeType.COD)
                                {
                                    orderFin.DeductCodFeeStage = orderFeeStage;
                                }
                                

                                // Set original cashier if not set yet (first time collecting)
                                if (orderFin.OriginalCodCashierId == null)
                                {
                                    orderFin.OriginalCodCashierId = model.UpdatedBy;
                                    orderFin.OriginalCodCashierUsername = model.UpdatedByUserName;
                                    orderFin.CodCollectedTime = DateTimeOffset.UtcNow;
                                }
                                needUpdate = true;
                            }
                            if (order.SourceFee > 0)
                            {
                                orderFin.SourceFeeCashierId = model.UpdatedBy;
                                orderFin.SourceFeeCashierUsername = model.UpdatedByUserName;
                                orderFin.SourceFeeStage = orderFeeStage;

                                // Set original cashier if not set yet (first time collecting)
                                if (orderFin.OriginalSourceFeeCashierId == null)
                                {
                                    orderFin.OriginalSourceFeeCashierId = model.UpdatedBy;
                                    orderFin.OriginalSourceFeeCashierUsername = model.UpdatedByUserName;
                                    orderFin.SourceFeeCollectedTime = DateTimeOffset.UtcNow;
                                }
                                needUpdate = true;
                            }
                            if (order.DestFee > 0)
                            {
                                orderFin.DestFeeCashierId = model.UpdatedBy;
                                orderFin.DestFeeCashierUsername = model.UpdatedByUserName;
                                orderFin.DestFeeStage = orderFeeStage;

                                // Set original cashier if not set yet (first time collecting)
                                if (orderFin.OriginalDestFeeCashierId == null)
                                {
                                    orderFin.OriginalDestFeeCashierId = model.UpdatedBy;
                                    orderFin.OriginalDestFeeCashierUsername = model.UpdatedByUserName;
                                    orderFin.DestFeeCollectedTime = DateTimeOffset.UtcNow;
                                }
                                needUpdate = true;
                            }
                            if (needUpdate)
                            {
                                await _orderFinRepository.UpdateAsync(orderFin);
                            }
                        }

                        transaction.TransactionOrders.Add(transactionOrder);
                    }

                    await _transactionRepository.InsertAsync(transaction);

                    //Cập nhật số dư ví
                    wallet.Balance += model.TotalAmount;
                    await _walletRepository.UpdateAsync(wallet);



                    //commit
                    await dbtransaction.CommitAsync();

                    // Cập nhật OrderTimeTrack với thời gian nhận tiền (chỉ khi Stage = Head)
                    await UpdateOrderTimeTrackFinancialTimes(model, orderFeeStage, orderCodStage);

                    // Tạo hành trình cho các đơn hàng sau khi commit thành công
                    await CreateOrderTracksForCollectedOrders(model);

                    result.Result = Result.Success;
                    return result;
                }
                catch (Exception ex)
                {
                    await dbtransaction.RollbackAsync();
                    result.Result = Result.Failed;
                    result.Message = ex.Message;
                }
            }

            return result;
        }



        private async Task<OrderFeeStage> GetFeeStageByUserId(int userId)
        {
            var result = OrderFeeStage.Branch;
            var userRoles = await _userService.GetRolesAsync(userId);
            var headRoles = new List<string>() { "FOUNDER", "ACCOUNTANT" };
            var branchRoles = new List<string>() { "COORDINATOR" };
            if (userRoles.Any())
            {
                var upperUserRoles = userRoles.Select(u => u.ToUpper()).ToList();
                if (upperUserRoles.Any(r => headRoles.Contains(r)))
                {
                    return OrderFeeStage.Head;
                }
            }
            return result;
        }

        /// <summary>
        /// Cập nhật OrderTimeTrack với thời gian nhận tiền (chỉ khi Stage = Head)
        /// </summary>
        private async Task UpdateOrderTimeTrackFinancialTimes(CollectCashierModel model, OrderFeeStage orderFeeStage, OrderCodStage orderCodStage)
        {
            try
            {
                // Lấy thông tin OrderFin để kiểm tra PayFeeType
                var orderIds = model.OrderList.Select(o => o.Id).ToList();
                var orderFins = await _orderFinRepository.Query()
                    .Where(of => orderIds.Contains(of.Id))
                    .ToListAsync();

                var orderUpdates = new List<OrderTimeTrackUpdateModel>();

                foreach (var order in model.OrderList)
                {
                    // Chỉ cập nhật khi có tiền thu và Stage = Head
                    var hasSourceFee = order.SourceFee > 0;
                    var hasDestFee = order.DestFee > 0;
                    var hasCodAmount = order.CodAmount > 0;
                    
                    // TransferFee không được xử lý trong CashierService hiện tại
                    // vì TransferAmount là tổng tiền chuyển khoản, không phải TransferFee riêng lẻ
                    
                    // Kiểm tra xem có cần cập nhật HeadDeductCodFeeTime không
                    // Khi nhận tiền COD và PayFeeType = COD, thì đồng nghĩa với việc đã nhận tiền cước
                    var orderFin = orderFins.FirstOrDefault(of => of.Id == order.Id);
                    var hasDeductCodFee = hasCodAmount && orderFin?.PayFeeType == PayFeeType.COD;
                    
                    if (hasSourceFee || hasDestFee || hasCodAmount)
                    {
                        orderUpdates.Add(new OrderTimeTrackUpdateModel
                        {
                            OrderId = order.Id,
                            SourceFeeStage = hasSourceFee ? orderFeeStage : null,
                            DestFeeStage = hasDestFee ? orderFeeStage : null,
                            TransferFeeStage = null, // Không xử lý TransferFee trong CashierService
                            CodStage = hasCodAmount ? orderCodStage : null,
                            DeductCodFeeStage = hasDeductCodFee ? orderFeeStage : null
                        });
                    }
                }

                if (orderUpdates.Any())
                {
                    var result = await _orderTimeTrackService.UpdateFinancialTimesBatchAsync(
                        orderUpdates, 
                        model.UpdatedBy, 
                        model.UpdatedByUserName, 
                        DateTimeOffset.Now);

                    if (result.Result != Result.Success)
                    {
                        // Log warning nhưng không throw exception để không ảnh hưởng đến flow chính
                        Console.WriteLine($"Warning: Không thể cập nhật OrderTimeTrack: {result.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                // Log error but don't throw to avoid affecting main flow
                Console.WriteLine($"Warning: Exception in UpdateOrderTimeTrackFinancialTimes: {ex.Message}");
            }
        }

        private async Task<OrderCodStage> GetCodStageByUserId(int userId)
        {
            var result = OrderCodStage.Branch;
            var userRoles = await _userService.GetRolesAsync(userId);

            var headRoles = new List<string>() { "FOUNDER", "CO-FOUNDER", "ACCOUNTANT" };
            var branchRoles = new List<string>() { "COORDINATOR" };


            if (userRoles.Any())
            {
                var upperUserRoles = userRoles.Select(u => u.ToUpper()).ToList();
                if (upperUserRoles.Any(r => headRoles.Contains(r)))
                {
                    return OrderCodStage.Head;
                }
            }
            return result;
        }

        /// <summary>
        /// Tạo hành trình cho các đơn hàng đã thu tiền
        /// </summary>
        /// <param name="model">Model thu tiền</param>
        private async Task CreateOrderTracksForCollectedOrders(CollectCashierModel model)
        {
            try
            {
                var orderIds = model.OrderList.Select(o => o.Id).ToList();
                var orders = await _orderRepository.Query()
                    .Where(o => orderIds.Contains(o.Id))
                    .ToListAsync();

                var trackModels = new List<OrderTrackModel>();
                var fromUser = await _userService.GetByIdAsync(model.FromUserId);
                foreach (var orderItem in model.OrderList)
                {
                    var order = orders.FirstOrDefault(o => o.Id == orderItem.Id);
                    if (order == null) continue;

                    var collectDetails = new List<string>();

                    // Tạo thông tin chi tiết về số tiền thu
                    if (orderItem.CodAmount > 0)
                    {
                        collectDetails.Add($"COD: {orderItem.CodAmount.FormatCurrency()}");
                    }
                    if (orderItem.SourceFee > 0)
                    {
                        collectDetails.Add($"Phí lấy: {orderItem.SourceFee.FormatCurrency()}");
                    }
                    if (orderItem.DestFee > 0)
                    {
                        collectDetails.Add($"Phí giao: {orderItem.DestFee.FormatCurrency()}");
                    }

                    var totalCollected = orderItem.CodAmount + orderItem.SourceFee + orderItem.DestFee;
                    var collectDetailStr = string.Join(", ", collectDetails);

                    var trackModel = new OrderTrackModel()
                    {
                        OrderId = order.Id,
                        OrderCode = order.Code,
                        Name = $"{model.UpdatedByUserName} thu tiền {totalCollected.FormatCurrency()} ({collectDetailStr})",
                        OrderTrackType = OrderTrackType.ForInternal,
                        TrackActionType = TrackActionType.CollectMoney,
                        PicId = fromUser.Id,
                        PicUserName = fromUser.UserName,
                        UpdatedBy = model.UpdatedBy,
                        UpdatedByUserName = model.UpdatedByUserName,
                        ActionSource = ActionSource.InternalWeb,
                        PhoneContact = ""
                    };
                    trackModels.Add(trackModel);
                }

                if (trackModels.Count > 0)
                {
                    await _orderTrackService.CreateAsync(trackModels);
                }
            }
            catch (Exception)
            {
                // Log error nhưng không throw để không ảnh hưởng đến transaction chính
                // TODO: Add logging here
                // _logger.LogError(ex, "Error creating order tracks for collected orders");
            }
        }
    }
}