using Dapper;
using RO.RedonePortal.App.GUI.Models.SMSGatewayMoParserForm;
using RO.RedonePortal.Persistence.Factory;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RO.RedonePortal.App.GUI
{
    public partial class SMSGatewayMoParserForm : Form
    {
        private bool keepRunning = false;
        private Thread processThread;
        private enum CdrState { PendingForDN = 0, WaitingtoDeliver = 1, Delivered = 2, Fail = -1 }
        private enum MoState { Pending = 0, Processing = 1, Processed = 2, Rejected = -1 }
        private enum ApplicationState { Pending = 0, Processing = 1, Processed = 2, Rejected = -1 }
        private enum PartnerState { Pending = 0, Processing = 1, Processed = 2, Rejected = -1 }

        public SMSGatewayMoParserForm()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            SafeInvoke(() => UpdateFormTitle($"SMSGatewayMoParser Stopped"));
            toolStripStatusLabel1.BackColor = Color.Red;
            toolStripStatusLabel1.Text = "STOPPED";
        }

        protected override async void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            keepRunning = false;
            if (processThread != null && processThread.IsAlive)
            {
                await Task.Run(() => processThread.Join());
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            SafeInvoke(() => lstError.Items.Clear());
        }

        private void btnToggle_Click(object sender, EventArgs e)
        {
            string[] moString_seperator = { " " };
            string statusCode = "";
            string resultCode, resultMsg;

            if (sender is Button button)
            {
                if (keepRunning)    //toggle to stop
                {
                    // Toggle to stop
                    keepRunning = false;
                    btnToggle.Text = "Start"; // Update button text to indicate action
                    SafeInvoke(() => UpdateFormTitle($"SMSGatewayMoParser Stopping"));
                    toolStripStatusLabel1.BackColor = Color.Yellow;
                    toolStripStatusLabel1.Text = "STOPPING";
                }
                else
                {
                    // Restart the thread
                    keepRunning = true;
                    btnToggle.Text = "Stop"; // Update button text to indicate action

                    toolStripStatusLabel1.BackColor = Color.Green;
                    toolStripStatusLabel1.Text = "STARTED";

                    processThread = new Thread(() =>
                    {
                        while (keepRunning)
                        {
                            SafeInvoke(() => UpdateFormTitle("SMSGatewayMoParser Processing..."));

                            try
                            {
                                using (var conn = ConnectionFactory.GetOpenConnection())
                                {
                                    var dyparam = new DynamicParameters();
                                    dyparam.Add("StatusCode", DBNull.Value, DbType.AnsiString, ParameterDirection.Output, 200);
                                    var moDataList = conn.Query<MoData>("gwSpoolMo_Get", dyparam, commandType: CommandType.StoredProcedure);

                                    foreach (var moData in moDataList)
                                    {
                                        moData.molevel = "SMSMO-L2";

                                        try
                                        {
                                            ProcessMoData(conn, moData);
                                        }
                                        catch (Exception ex)
                                        {
                                            LogError(lstError, $"Processing MoData failed: {moData.SessionID}, Error: {ex.Message}");
                                            UpdateMoDataState(conn, moData, "REJECTED", "PROCESSING_ERROR", CdrState.Fail, ApplicationState.Rejected, PartnerState.Rejected);
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                SafeInvoke(() => lstError.Items.Add(ex.Message));
                            }

                            for (int i = 5; i > 0; i--)
                            {
                                SafeInvoke(() => UpdateFormTitle($"SMSGatewayMoParser Sleep countdown: {i} seconds remaining..."));
                                Thread.Sleep(1000); // Wait for 1 second
                            }
                        }

                        SafeInvoke(() => UpdateFormTitle($"SMSGatewayMoParser Stop"));
                        SafeInvoke(() => toolStripStatusLabel1.BackColor = Color.Red);
                        SafeInvoke(() => toolStripStatusLabel1.Text = "STOPPED");
                        ;
                    });

                    processThread.Start();

                    btnToggle.Text = "Pause";
                }
            }
        }

        // Helper method to safely invoke actions on the UI thread
        private void SafeInvoke(Action action)
        {
            if (IsHandleCreated && !IsDisposed)
            {
                try { Invoke(action); } catch (ObjectDisposedException) { }
            }
        }

        // Helper method to update the form title safely
        private void UpdateFormTitle(string title)
        {
            this.Text = title;
        }

        private void ProcessMoData(IDbConnection conn, MoData moData)
        {
            var shortcodeConfig = GetShortcodeConfigs(conn, moData.Shortcode);

            if (shortcodeConfig == null)
            {
                LogError(lstError, $"No shortcode configuration found for: {moData.SessionID}");
                UpdateMoDataState(conn, moData, "REJECTED", "NO_SHORTCODE_CONFIG_FOUND", CdrState.Fail, ApplicationState.Rejected, PartnerState.Rejected);
                return;
            }

            try
            {
                InsertTrafficHistory(conn, moData, shortcodeConfig);

                if (!ValidateMsisdn(moData))
                {
                    UpdateMoDataState(conn, moData, "REJECTED", "INVALID_MSISDN", CdrState.Fail, ApplicationState.Rejected, PartnerState.Rejected);
                    throw new Exception("Msisdn validation failed.");
                }

                var serviceType = DetermineServiceType(conn, moData);
                moData.serviceType = serviceType.ServiceType;
                moData.keyword = serviceType.Keyword;
                shortcodeConfig.MoPrice = serviceType.MoPrice;

                if (shortcodeConfig.IsMoCDRRequired && moData.Channel == "SMS")
                {
                    ProcessCdr(conn, moData, shortcodeConfig);
                    moData.moCdrForwardState = (int)CdrState.WaitingtoDeliver; // Set initial state for CDR
                    moData.MoBillingCode = "DELIVERED";
                    moData.MoBillingStatus = "DELIVERED";
                }

                if (shortcodeConfig.IsApplicationProcess)
                {
                    ProcessApplication(conn, moData);
                    moData.applicationProcessState = (int)ApplicationState.Processed; // Set initial state for Application
                }

                UpdateMoDataState(conn, moData, moData.MoBillingCode, moData.MoBillingStatus, (CdrState)moData.moCdrForwardState, (ApplicationState)moData.applicationProcessState, (PartnerState)moData.partnerForwardState);
            }
            catch (Exception ex)
            {
                LogError(lstError, $"Error processing shortcode config: {moData.Shortcode}, Error: {ex.Message}");
            }
        }

        private bool ValidateMsisdn(MoData moData)
        {
            return true;
        }

        private ShortcodeConfig GetShortcodeConfigs(IDbConnection conn, string shortcode)
        {
            var dyparam = new DynamicParameters();
            dyparam.Add("Shortcode", shortcode);

            return conn.QueryFirstOrDefault<ShortcodeConfig>("shortcodeConfig_Get", dyparam, commandType: CommandType.StoredProcedure);
        }

        private void InsertTrafficHistory(IDbConnection conn, MoData moData, ShortcodeConfig shortcodeConfig)
        {
            var dyparam = new DynamicParameters();
            dyparam.Add("SessionID", moData.SessionID);
            dyparam.Add("Operator", moData.Operator);
            dyparam.Add("Msisdn", moData.Msisdn);
            dyparam.Add("Shortcode", moData.Shortcode);
            dyparam.Add("MoType", moData.Channel);
            dyparam.Add("TrxID", moData.OperatorTrxID);
            dyparam.Add("MoMessage", moData.MoString);
            dyparam.Add("MoPrice", shortcodeConfig.MoPrice);
            dyparam.Add("EntryDatetime", moData.CreatedTimestamp);

            conn.Execute("traffichistory_InsertNew", dyparam, commandType: CommandType.StoredProcedure);
            LogInfo(lstInfo, $"Traffic history inserted: {moData.SessionID}");
        }

        private (bool IsValid, string ServiceType, string Keyword, string MoPrice) DetermineServiceType(IDbConnection conn, MoData moData)
        {
            var dyparam = new DynamicParameters();
            dyparam.Add("Shortcode", moData.Shortcode);

            var serviceTypes = conn.Query<ServiceTypeMatcher>("SmsGatewayMoDriver_ServiceTypeMatcher_Get", dyparam, commandType: CommandType.StoredProcedure)?.ToList();

            if (serviceTypes == null || !serviceTypes.Any())
            {
                return (false, "IOD", moData.MoString, "0");
            }

            foreach (var serviceType in serviceTypes)
            {
                if (Regex.IsMatch(moData.MoString, serviceType.RegexMatch, RegexOptions.IgnoreCase))
                {
                    var keyword = serviceType.DefaultKeyword ?? moData.MoString.Split(' ')[0];
                    return (true, serviceType.ServiceType, keyword, serviceType.MoCdrPrice);
                }
            }

            return (true, "IOD", moData.MoString.Split(' ')[0], "0");
        }

        private void ProcessCdr(IDbConnection conn, MoData moData, ShortcodeConfig shortcodeConfig)
        {
            var dyparam = new DynamicParameters();
            dyparam.Add("SessionID", moData.OperatorTrxID);
            dyparam.Add("Keyword", moData.keyword);
            dyparam.Add("PartnerCdrID", shortcodeConfig.MoCdrType);
            dyparam.Add("CdrMethod", shortcodeConfig.MoCdrMethod);
            dyparam.Add("Msisdn", moData.Msisdn);
            dyparam.Add("CreatedTimestamp", moData.CreatedTimestamp);
            dyparam.Add("CdrNum", "1");
            dyparam.Add("CdrShortcode", moData.Shortcode);
            dyparam.Add("CdrAdditionalInfo", shortcodeConfig.MoCdrInfo);
            dyparam.Add("CdrPrice", shortcodeConfig.MoPrice);
            dyparam.Add("IsProcessed", 1);

            conn.Execute("cdrSpool_InsertNew", dyparam, commandType: CommandType.StoredProcedure);
            LogInfo(lstInfo, $"CDR inserted: {moData.SessionID}");
        }

        private void ProcessApplication(IDbConnection conn, MoData moData)
        {
            var dyparam = new DynamicParameters();
            dyparam.Add("MoOrderID", moData.SessionID);
            dyparam.Add("Operator", moData.Operator);
            dyparam.Add("Msisdn", moData.Msisdn);
            dyparam.Add("Shortcode", moData.Shortcode);
            dyparam.Add("MoType", moData.Channel);
            dyparam.Add("TrxID", moData.OperatorTrxID);
            dyparam.Add("MoMessage", moData.MoString);
            dyparam.Add("ServiceType", moData.serviceType);
            dyparam.Add("Keyword", moData.keyword);
            dyparam.Add("EntryDateTime", moData.CreatedTimestamp);
            dyparam.Add("StatusCode", DBNull.Value, DbType.AnsiString, ParameterDirection.Output, 25);

            conn.Execute("appSpoolMo_InsertNew", dyparam, commandType: CommandType.StoredProcedure);
            LogInfo(lstInfo, $"Application processed: {moData.SessionID}");
        }

        private void UpdateMoDataState(IDbConnection conn, MoData moData, string billingCode, string billingStatus, CdrState cdrState, ApplicationState appState, PartnerState partnerState)
        {
            var dyparam = new DynamicParameters();
            dyparam.Add("SessionID", moData.SessionID);
            dyparam.Add("MoBillingDeliveryCode", billingCode);
            dyparam.Add("MoBillingDeliveryStatus", billingStatus);
            dyparam.Add("MoCdrForwardState", cdrState);
            dyparam.Add("ApplicationProcessState", appState);
            dyparam.Add("PartnerForwardState", partnerState);

            conn.Execute("gwSpoolMo_Update", dyparam, commandType: CommandType.StoredProcedure);
            LogInfo(lstInfo, $"MoData state updated: {moData.SessionID}");
        }

        private void LogError(ListBox lstError, string message)
        {
            SafeInvoke(() => lstError.Items.Add(message));
        }

        private void LogInfo(ListBox lstInfo, string message)
        {
            SafeInvoke(() => lstInfo.Items.Add(message));
        }
    }
}