using Dapper;
using Microsoft.Extensions.Configuration;
using Oracle.ManagedDataAccess.Client;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.ServiceModel;
using System.Threading.Tasks;

namespace SicboSub.StartProcedure
{
    public class ExchangeService
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(ExchangeService));
        private readonly string _connectionString;
        SicboSubWss.SicboSubWsClient wsClient;

        string wsUser;
        string wsPass;
        string wsPackage;
        protected void SetWsClient(ref SicboSubWss.SicboSubWsClient wsClient, IConfiguration configuration)
        {
            wsClient.Endpoint.Address = new EndpointAddress(configuration.GetSection("wsUrl").Value);
        }
        public ExchangeService(string connectionString, IConfiguration configuration)
        {
            _connectionString = connectionString;
            wsUser = configuration["wsUser"];
            wsPass = configuration["wsPass"];
            wsPackage = configuration["SERVICE_ID"];
            wsClient = new SicboSubWss.SicboSubWsClient();
            SetWsClient(ref wsClient, configuration);
        }

        public async Task ExecuteExchangeProcessingAsync()
        {
            log.Info($"[BẮT ĐẦU] Quét trả thưởng đổi quà (ExchangeHistory)...");

            using (var conn = new OracleConnection(_connectionString))
            {
                await conn.OpenAsync();

                try
                {
                    // 1. QUÉT ALL PENDING (Status=0)
                    string sqlGetPending = @"
                        SELECT ID, MSISDN, MONEY_ADDED, COIN_DEDUCTED
                        FROM EXCHANGE_HISTORY 
                        WHERE STATUS = 0
                        ORDER BY ID ASC"; // FIFO

                    var pendingExchanges = await conn.QueryAsync<ExchangeHistoryDto>(sqlGetPending);
                    var exchangeList = pendingExchanges.ToList();
                    
                    if (exchangeList.Count == 0)
                    {
                        log.Info(" -> Không có yêu cầu đổi thưởng nào cần xử lý.");
                        return;
                    }

                    log.Info($" -> Tìm thấy {exchangeList.Count} yêu cầu đang chờ (Status=0).");

                    // 2. XỬ LÝ AN TOÀN (Claim -> Process -> Finish)
                    foreach (var item in exchangeList)
                    {
                        await ProcessSingleExchangeSafe(conn, item);
                    }
                }
                catch (Exception ex)
                {
                    log.Error($"[LỖI NGHIÊM TRỌNG] {ex.Message}");
                }
            }
            log.Info("[HOÀN TẤT] Quá trình đổi thưởng kết thúc.\n");
        }

        /// <summary>
        /// Xử lý từng giao dịch với cơ chế 'Chiếm quyền' (Optimistic Locking) tránh tranh chấp.
        /// </summary>
        private async Task ProcessSingleExchangeSafe(OracleConnection conn, ExchangeHistoryDto item)
        {
            // BƯỚC 1: CHIẾM QUYỀN (Mark as Processing - 3)
            // Chỉ update được nếu Status vẫn đang là 0.
            string sqlClaim = "UPDATE EXCHANGE_HISTORY SET STATUS = 3 WHERE ID = :id AND STATUS = 0";
            int rowsAffected = await conn.ExecuteAsync(sqlClaim, new { id = item.ID });

            if (rowsAffected == 0)
            {
                // Ai đó đã xử lý rồi, hoặc trạng thái đã thay đổi -> Bỏ qua
                log.Warn($"    ! Bỏ qua ID {item.ID} (User {item.MSISDN}) - Đã bị chiếm bởi tiến trình khác.");
                return;
            }

            // BƯỚC 2: THỰC HIỆN TRẢ THƯỞNG (Payment Logic)
            log.Info($"    -> Đang xử lý ID {item.ID} (User {item.MSISDN}): Topup {item.MONEY_ADDED}...");
            bool isSuccess = await DistributeMoney(item);

            // BƯỚC 3: CHỐT TRẠNG THÁI
            // Nếu thành công -> 1, Nếu thất bại -> 2
            // Đảm bảo không retry lại nếu status = 2.
            int finalStatus = isSuccess ? 1 : 2; 

            // Cập nhật lại Status
            string sqlFinalize = "UPDATE EXCHANGE_HISTORY SET STATUS = :s WHERE ID = :id";
            await conn.ExecuteAsync(sqlFinalize, new { s = finalStatus, id = item.ID });

            log.Info($"       -> KẾT QUẢ: {(isSuccess ? "THÀNH CÔNG" : "THẤT BẠI (Đã set status=2 để không retry)")}");
        }

        private async Task<bool> DistributeMoney(ExchangeHistoryDto item)
        {
            try
            {
                if (item.MONEY_ADDED <= 0) return true;

                // Gọi web service AddMoney
                SicboSubWss.wsAddMoneyResponse response = await wsClient.wsAddMoneyAsync(
                    wsUser,
                    wsPass,
                    item.MSISDN,
                    wsPackage,
                    item.MONEY_ADDED.ToString(),
                    wsPackage,
                    null, // Transaction/Service info
                    "Exchange" // Additional info if needed
                );

                // Kiểm tra kết quả trả về
                // Giả sử errorCode = "0" là thành công, cần verify với định nghĩa WSDL/Class sinh ra
                if (response != null && response.@return != null && response.@return.errorCode == "0")
                {
                    return true;
                }
                
                log.Error($"       [API ERROR] ErrorCode: {response?.@return?.errorCode}, Message: {response?.@return?.content}");
                return false;
            }
            catch (Exception ex)
            {
                log.Error($"       [LỖI API] {ex.Message}");
                return false;
            }
        }
    }

    public class ExchangeHistoryDto
    {
        public decimal ID { get; set; }
        public string MSISDN { get; set; } = "";
        public decimal MONEY_ADDED { get; set; }
        public decimal COIN_DEDUCTED { get; set; }
    }
}
