using System;
using System.Data;
using Dapper;
using Oracle.ManagedDataAccess.Client;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;

namespace SicboSub.StartProcedure
{
    public class RankingService
    {
        private readonly string _connectionString;

        public RankingService(string connectionString)
        {
            _connectionString = connectionString;
        }

        public async Task ExecuteDailyRankingAsync(DateTime reportDate)
        {
            await RunRankingProcess(reportDate, "DAILY");
        }

        public async Task ExecuteMonthlyRankingAsync(DateTime reportDate)
        {
            await RunRankingProcess(reportDate, "MONTHLY");
        }

        private async Task RunRankingProcess(DateTime reportDate, string type)
        {
            string typeVN = type == "DAILY" ? "NGÀY" : "THÁNG";
            Console.WriteLine($"[BẮT ĐẦU] Tính toán BXH {typeVN} cho ngày {reportDate:yyyy-MM-dd}...");

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

                try
                {
                    // 1. Gọi Procedure: Sinh ra dữ liệu SNAPSHOT (Nếu đã chạy Success thì nó sẽ skip)
                    var p = new DynamicParameters();
                    p.Add("p_report_date", reportDate, DbType.Date, ParameterDirection.Input);
                    p.Add("p_type", type, DbType.String, ParameterDirection.Input);

                    await conn.ExecuteAsync("PRC_CALCULATE_RANKING", p, commandType: CommandType.StoredProcedure);
                    Console.WriteLine(" -> Kiểm tra/Chạy Procedure hoàn tất.");

                    // 2. QUÉT ALL PENDING (Kể cả những cái bị treo từ lần chạy cũ bị crash)
                    // Không chỉ lấy của ngày hôm nay mà lấy tất cả status=0 để xử lý triệt để
                    string sqlGetPending = @"
                        SELECT ID, MSISDN, REWARD_VALUE, REWARD_UNIT, NOTE 
                        FROM RANKING_REWARD_LOG 
                        WHERE STATUS = 0
                        ORDER BY ID ASC"; // FIFO

                    var pendingRewards = await conn.QueryAsync<RankingRewardLog>(sqlGetPending);
                    var rewardList = pendingRewards.ToList();
                    
                    if (rewardList.Count == 0)
                    {
                        Console.WriteLine(" -> Không có phần thưởng nào cần xử lý.");
                        return;
                    }

                    Console.WriteLine($" -> Tìm thấy {rewardList.Count} lệnh thưởng đang chờ (Status=0).");

                    // 3. XỬ LÝ AN TOÀN (Claim -> Process -> Finish)
                    foreach (var reward in rewardList)
                    {
                        await ProcessSingleRewardSafe(conn, reward);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"[LỖI NGHIÊM TRỌNG] {ex.Message}");
                }
            }
            Console.WriteLine("[HOÀN TẤT] Quá trình 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 ProcessSingleRewardSafe(OracleConnection conn, RankingRewardLog reward)
        {
            // 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 RANKING_REWARD_LOG SET STATUS = 3 WHERE ID = :id AND STATUS = 0";
            int rowsAffected = await conn.ExecuteAsync(sqlClaim, new { id = reward.ID });

            if (rowsAffected == 0)
            {
                // Ai đó đã xử lý rồi, hoặc trạng thái đã thay đổi -> Bỏ qua
                Console.WriteLine($"    ! Bỏ qua ID {reward.ID} (User {reward.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)
            Console.WriteLine($"    -> Đang xử lý ID {reward.ID} (User {reward.MSISDN}): {reward.REWARD_VALUE} {reward.REWARD_UNIT}...");
            bool isSuccess = await DistributeReward(reward);

            // BƯỚC 3: CHỐT TRẠNG THÁI (1: Success, 2: Failed)
            // Nếu thất bại, set về 2 (Failed) để admin check, hoặc set về 0 (Pending) để retry tự động tùy nghiệp vụ.
            // Ở đây tôi set về 2 để tránh retry vô tận nếu lỗi logic.
            int finalStatus = isSuccess ? 1 : 2; 

            string sqlFinalize = "UPDATE RANKING_REWARD_LOG SET STATUS = :s, CREATED_DATE = SYSDATE WHERE ID = :id";
            await conn.ExecuteAsync(sqlFinalize, new { s = finalStatus, id = reward.ID });

            Console.WriteLine($"       -> KẾT QUẢ: {(isSuccess ? "THÀNH CÔNG" : "THẤT BẠI")}");
        }

        private async Task<bool> DistributeReward(RankingRewardLog reward)
        {
            try
            {
                // TODO: Gọi API, Cộng tiền thật ở đây
                // Giả lập độ trễ mạng
                await Task.Delay(50); 
                
                if (reward.REWARD_UNIT == "COIN")
                {
                    // Call Service Add Coin
                }
                else if (reward.REWARD_UNIT == "MONEY")
                {
                    // Call Service Add Money
                }

                return true; // Giả sử luôn thành công
            }
            catch (Exception ex)
            {
                Console.WriteLine($"       [LỖI API] {ex.Message}");
                return false;
            }
        }
    }

    public class RankingRewardLog
    {
        public decimal ID { get; set; }
        public string MSISDN { get; set; } = "";
        public decimal REWARD_VALUE { get; set; }
        public string REWARD_UNIT { get; set; } = "";
        public string NOTE { get; set; } = "";
    }
}
