using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;

namespace bdf
{
    public class MegaT
    {
        // A class to hold the extracted data for a single row of INSIS data
        public class INSISData
        {
            public string Item { get; set; }
            public string Code { get; set; }
            public string Description { get; set; }
            public string Period { get; set; }
            public string UnitPrice { get; set; }
            public string Quantity { get; set; }
            public string Amount { get; set; }
            public string BillingType { get; set; }
            public string Currency { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }

            public override string ToString()
            {
                return $"Code: {Code}, Description: {Description}, Unit Price: {UnitPrice}, Qty: {Quantity}, Amount: {Amount}, Start Date: {StartDate}";
            }
        }

        public static List<INSISData> GetRVNUDetails_bySCHED(string sched, ref CookieContainer cookies)
        {
            bool success = false;
            byte[] webResp = null;

            List<INSISData> rvnu_items = new List<INSISData> { };

            success = Web.MakeRequest(
                "GET",
                "http://megatool.rogers.com/megatool/megatool/INSIS/insisMainwindow.asp?serv_id=" + sched,
                false,
                "",
                ref webResp,
                ref cookies);

            if (!success)
            {
                Logger.Log(5, "Get INSIS RVNU TopLevel Details FAIL {0}", sched);
            }

            string html = Encoding.ASCII.GetString(webResp);


            if (html.Length > 5000) // valid SCHED, seek RVNU items for SIP data
            {
                try
                {
                    HashSet<string > rvnu_links = RVNU_item_links(html);

                    foreach (var link in rvnu_links)
                    {
                        success = false;
                        webResp = null;

                        success = Web.MakeRequest(
                            "GET",
                            "http://megatool.rogers.com/megatool/megatool/INSIS/" + link.ToString(),
                            false,
                            "",
                            ref webResp,
                            ref cookies);

                        if (!success)
                        {
                            Logger.Log(5, "Get INSIS RVNU Details FAIL {0}-{1} {2}", sched, link, webResp);
                        }

                        string html2 = Encoding.ASCII.GetString(webResp);
                        INSISData x = ExtractINSISData(html2);
                        if (!x.BillingType.Contains("Delete"))      // Feb 12 2026
                            rvnu_items.Add(x);
                        Logger.Log(6, "Desc: {0} | rvnu_items size={1}", x.Description, rvnu_items.Count);

                    }

                }
                catch (Exception e)
                {
                    Logger.Log(0, "Error SCHED {0} - SIP HTML={0}", sched, e.Message);
                }
            }
            return rvnu_items;
        }

        public static HashSet<string> RVNU_item_links(string html)
        {
            HashSet<string> result = new HashSet<string>(System.StringComparer.OrdinalIgnoreCase);

            if (string.IsNullOrWhiteSpace(html))
                return result;

            string trPattern =
                @"<tr[^>]*id\s*=\s*['""]R\d+['""][^>]*>(.*?)</tr>";

            System.Text.RegularExpressions.MatchCollection trMatches =
                System.Text.RegularExpressions.Regex.Matches(
                    html,
                    trPattern,
                    System.Text.RegularExpressions.RegexOptions.IgnoreCase |
                    System.Text.RegularExpressions.RegexOptions.Singleline
                );

            foreach (System.Text.RegularExpressions.Match trMatch in trMatches)
            {
                string trContent = trMatch.Groups[1].Value;

                System.Text.RegularExpressions.MatchCollection tdMatches =
                    System.Text.RegularExpressions.Regex.Matches(
                        trContent,
                        @"<td[^>]*>(.*?)</td>",
                        System.Text.RegularExpressions.RegexOptions.IgnoreCase |
                        System.Text.RegularExpressions.RegexOptions.Singleline
                    );

                if (tdMatches.Count < 5)
                    continue;

                // ----- Extract openIt('...') from 4th <td> -----
                string fourthTd = tdMatches[3].Groups[1].Value;
                //Logger.Log(6,"4th [{0}]", fourthTd);

                string openItValue = "";
                if (fourthTd.IndexOf("RVNU", System.StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    System.Text.RegularExpressions.Match onclickMatch =
                    System.Text.RegularExpressions.Regex.Match(
                        fourthTd,
                        @"openIt\('(?<openit>[^']+)'\)",
                        System.Text.RegularExpressions.RegexOptions.IgnoreCase |
                        System.Text.RegularExpressions.RegexOptions.Singleline
                    );

                    if (!onclickMatch.Success)
                    {
                        Logger.Log(3,"no rvnu match");
                        continue;
                    }
                    openItValue = onclickMatch.Groups["openit"].Value.Trim();
                }
                else  // Skip non-RVNU rows
                {
                    Logger.Log(4,"non-revenue [{0}]", fourthTd);
                    continue;
                }

                result.Add(openItValue);
            }

            return result;
        }

        public static INSISData ExtractINSISData(string html)
        {
            var extractedData = new INSISData();

            // Regex to find table rows (<tr>) within a specific table structure (adjust if needed, e.g., using a table ID)
            // The pattern uses capturing groups for each <td> content
            // It assumes <td> tags might have extra spaces or attributes, but the content inside is the target.

            Dictionary<string, string> result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            if (string.IsNullOrWhiteSpace(html))
                return null;

            string pattern =
                @"<td[^>]*class\s*=\s*""heading""[^>]*>\s*(?<heading>[^<]+)\s*</td>[\s\S]*?" +
                @"value\s*=\s*""(?<value>[^""]*)""";

            System.Text.RegularExpressions.MatchCollection matches =
                System.Text.RegularExpressions.Regex.Matches(
                    html,
                    pattern,
                    System.Text.RegularExpressions.RegexOptions.IgnoreCase);

            foreach (System.Text.RegularExpressions.Match match in matches)
            {
                string heading = match.Groups["heading"].Value.Trim();
                string value = match.Groups["value"].Value.Trim().Replace("$","").Replace("/", "-"); // FIXME convert to SSC style date
                
                if (!result.ContainsKey(heading))
                {
                    result.Add(heading.Replace(" ", ""), value);        //Remove field label spaces to make them valid dictionary entries
                    Logger.Log(5, "Found: {0}={1}", heading, value);
                }
            }

            System.Type type = typeof(INSISData);

            foreach (System.Reflection.PropertyInfo prop in type.GetProperties(
                System.Reflection.BindingFlags.Instance |
                System.Reflection.BindingFlags.Public))
            {
                Logger.Log(6,"a4n {0} : {1}", prop.PropertyType.Name, prop.Name);
                if (prop.PropertyType != typeof(string))
                    
                    continue;

                if (!prop.CanWrite)
                    continue;

                if (result.TryGetValue(prop.Name, out string value))
                {
                    Logger.Log(6,"a5 {0}", value);
                    try
                    {
                        prop.SetValue( extractedData, (string)value);
                    }
                    catch (Exception e)
                    {
                        Logger.Log(0, "Error prop.SetValue {0}:{1}\n{2}", prop.Name, value, e.Message);
                    }
                }
            }
            return extractedData;
        }

        // Takes MM-d-yyyy and converts to yyyyMMdd
        public static string ConvertDate(string input)
        {
            System.DateTime date =
                System.DateTime.ParseExact(
                    input,
                    "M-d-yyyy",
                    System.Globalization.CultureInfo.InvariantCulture
                );

            return date.ToString("yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
        }

        //------------------------------------
        // SCHED-A Info Extraction from "Schedule Details" Megatool page, most import is SO<->SCHED mapping
        public static Dictionary<string, string> GetParms(string sched, ref CookieContainer cookies)
        {
            string token = "";
            bool success = false;
            byte[] webResp = null;
            string html = "";

            Dictionary<string, string> parms = new Dictionary<string, string> { };

            //if (sched != "dummy")
            if ( sched.Length != 3 ) // not a 'dummy' request
            {
                success = Web.MakeRequest(
                    "GET",
                    "http://megatool.rogers.com/megatool/megatool/INSIS/schedule.asp?logo=N&serv_id=" + sched,
                    false,
                    "",
                    ref webResp,
                    ref cookies);

                if (!success)
                {
                    Logger.Log(0, "GetParms fail{0}", token);
                }

                //Console.WriteLine(Encoding.ASCII.GetString(webResp));

                html = Encoding.ASCII.GetString(webResp);
            }
            else
            {
                success = true;
                html = "";
            }

            //following builds the columns in order, one IF per column
            try
            {
                // SO + up to 5 non-digits + six digits
                var regex = new Regex(@"SO(?:\D{0,5})(\d{6})", RegexOptions.IgnoreCase); 

                Match m = regex.Match(html);
                if (m.Success)
                {
                    // m.Value = the entire match
                    parms.Add("SO", m.Groups[1].ToString()); // = the 6-digit number
                }
                else
                {
                    parms.Add("SO", "XXX");
                    
                }
                
                // TA + up to 5 non-digits + R + nine digits
                regex = new Regex(@"TA(?:\D{0,5})(\d{9})", RegexOptions.IgnoreCase); 

                m = regex.Match(html);
                if (m.Success)
                {
                    // m.Value = the entire match
                    parms.Add("TA", "R"+m.Groups[1].ToString()); // = the 6-digit number
                }
                else
                {
                    parms.Add("TA", "XXX");
                }

                // COP + up to 5 non-digits + six digits
                regex = new Regex(@"COP(?:\D{0,5})(\d{6})", RegexOptions.IgnoreCase);

                m = regex.Match(html);
                if (m.Success)
                {
                    // m.Value = the entire match
                    parms.Add("COP", m.Groups[1].ToString()); // = the 6-digit number
                }
                else
                {
                    parms.Add("COP", "XXX");
                }


                // PRID + up to 5 non-digits + four digits
                //regex = new Regex(@"PR.*ID(?:\D{0,5})(\d{4})", RegexOptions.IgnoreCase);
                regex = new Regex(@"PR(?:\s?ID)?(?:\D{0,5})(\d{4})", RegexOptions.IgnoreCase);

                m = regex.Match(html);
                if (m.Success)
                {
                    // m.Value = the entire match
                    parms.Add("PRID", m.Groups[1].ToString()); // = the 6-digit number
                }
                else
                {
                    parms.Add("PRID", "XXX");
                }

                // Contract + up to 5 non-digits + letter + 9 digits
                regex = new Regex(@"CONTRACT(?:\D{0,5})([a-zA-Z]\d{9})", RegexOptions.IgnoreCase); 

                m = regex.Match(html);
                if (m.Success)
                {
                    // m.Value = the entire match
                    parms.Add("CONTRACT", m.Groups[1].ToString()); // = the alpha+9-digit number
                }
                else
                {
                    parms.Add("CONTRACT", "XXX");
                }

                //
                parms.Add("SCHED", sched); // needed to build that datatable correctly in MAIN
                //

                // Billing Start Date; seems to come in Mon D, YYYY format, need to convert to YYYYMMDD
                //regex = new Regex(@"<br>Bill Start Date:\s+([a-zA-Z0-9,\s]+[0-9]{4})[.]?\s*<br>", RegexOptions.IgnoreCase);
                // Bill(?:ing|in)?\sStart(?: Date)?:\s
                regex = new Regex(@"<br>Bill(?:ing|in)?\sStart(?: Date)?:\s+([a-zA-Z0-9,\s]+[0-9]{4})[.]?\s*<br>", RegexOptions.IgnoreCase);

                m = regex.Match(html);
                if (m.Success)
                {
                    Logger.Log(5, "raw BSD = |{0}|", m.Groups[1].ToString());
                    try
                    {
                        // m.Value = the entire match
                        
                        string input = m.Groups[1].ToString();
                        string iformat = "MMM d, yyyy";
                        string oformat = "yyyyMMdd";
                        DateTime fdate = DateTime.ParseExact(input, iformat, CultureInfo.InvariantCulture);

                        parms.Add("BSD", fdate.ToString(oformat)); // = the alpha+9-digit number
                        Logger.Log(4, "BSD = >{0}<", fdate.ToString(oformat));
                    }
                    catch (Exception e)
                    {
                        Logger.Log(0, "Error converting date from SchedA->Schedule details {0}", e.Message);
                    }
                }
                else
                {
                    parms.Add("BSD", "XXX");
                }

                // MRR+ up to 5 non-digits + $XXX.XX
                //regex = new Regex(@"(?<!Old\s)(?:New\s)?(?:MRR|MRC)[^0-9$]{0,5}\$?\s*([0-9,]+(?:\.\d{1,2})?)", RegexOptions.IgnoreCase);
                regex = new Regex(@"(?<!Old\s)(?:New\s)?(?:MRR|MRC)[^0-9$]{0,5}\$?(\d+(?:\.\d{1,2})?)", RegexOptions.IgnoreCase);

                m = regex.Match(html);
                if (m.Success)
                {
                    // m.Value = the entire match
                    string _mrr = m.Groups[1].ToString();
                    _mrr = Regex.Replace(_mrr, @"[^0-9.]", "");
                    parms.Add("MRR", _mrr); // = the alpha+9-digit number
                }
                else
                {
                    parms.Add("MRR", "XXX");
                }

                // NRC+ up to 5 non-digits + $XXX.XX
                regex = new Regex(@"(?:NRR|NRC)[^0-9$]{0,5}\$?\s*([0-9,]+(?:\.\d{1,2})?)", RegexOptions.IgnoreCase);
                //regex = new Regex(@"(?<!Old\s)(?:New\s)?(?:NRR|NRC)[^0-9$]{0,5}\$?\s*([0-9,]+(?:\.\d{1,2})?)", RegexOptions.IgnoreCase);
                
                m = regex.Match(html);
                if (m.Success)
                {
                    // m.Value = the entire match
                    try
                    {
                        string _nrc = m.Groups[1].ToString();
                        _nrc = Regex.Replace(_nrc, @"[^0-9.]", "");
                        parms.Add("NRC", String.Format( "{0:0.00}", _nrc)); // = decimal currency only
                    }
                    catch (Exception e)
                    {
                        Logger.Log(0, "NRC Parm: {0}:{1}", e.Message, e.InnerException.Message);
                    }
                }
                else 
                {
                    parms.Add("NRC", "XXX");
                }

                Logger.Log(2, "{0} : SO:{1} COP-{2} TA: {3} PRID: {4} Contract {5} BSD {6} MRR ${7} NRC ${8}", sched, parms["SO"], parms["COP"],
                                                                parms["TA"], parms["PRID"], parms["CONTRACT"], parms["BSD"], parms["MRR"], parms["NRC"] );
                //Logger.Log(1, "SO:{0}", parms["SO"]);
            }
            catch (Exception e)
            {
                Logger.Log(0, "Exception: {0}:{1}", e.Message, e.InnerException.Message);
            }

            if (sched.ToUpper() == "SIP")   // add SIP extension columns to Megatool table to track Sessions and Access costs
            {
                parms.Add("AQty", "XXX");
                parms.Add("ACst", "XXX");
                parms.Add("SQty", "XXX");
                parms.Add("SCst", "XXX");
            }

            return parms; // no match found
        }

        // Deal with filtering out which SCHEDs are actually active with a given prefix
        public static bool SchedXL(string sched, ref CookieContainer cookies)
        {
            string token = "";
            bool success = false;
            byte[] webResp = null;

            success = Web.MakeRequest(
                "GET",
                "http://megatool.rogers.com/megatool/megatool/INSIS/insisMainwindow.asp?serv_id=" + sched,
                false,
                "",
                ref webResp,
                ref cookies);

            if (!success)
            {
                Logger.Log(5, "Check Inactive SchedA's fail. {0}", token);
            }

            //Console.WriteLine(Encoding.ASCII.GetString(webResp));
            else
            {

                string html = Encoding.ASCII.GetString(webResp);

                if (html.Length < 5000) return true; // bit of a h*ck for when NSIS says 'no records'

                const string startKey = ">Order Type (";
                const string endKey = ")<br";

                //var results = new List<string>();
                int index = 0;

                while (true)
                {
                    // Find "serv_id="
                    int start = html.IndexOf(startKey, index, StringComparison.OrdinalIgnoreCase);
                    if (start == -1)
                        break; // no more matches

                    start += startKey.Length;

                    // Find "')"
                    int end = html.IndexOf(endKey, start, StringComparison.Ordinal);
                    if (end == -1)
                        break;

                    string value = html.Substring(start, end - start);
                    if ((value == "XL") || (value == "XP") || (value == "NX")) return true;

                    index = end + endKey.Length;
                }
            }
            return false;
        }

        public static bool GetSchedAs(string serv_id, ref List<string> scheds, ref CookieContainer cookies)
        {
            bool success = false;
            string token = "";

            byte[] webResp = null;

            success = Web.MakeRequest(
                "POST",
                "http://megatool.rogers.com/megatool/megatool/INSIS/SearchSites.asp?query=Y&exportFlag=N",
                "service_id="+serv_id,
                false,
                "",
                ref webResp,
                ref cookies);

            if (!success)
            {
                Logger.Log(0, "Get SchedA's Fail {0}", token);
            }

            //Console.WriteLine(Encoding.ASCII.GetString(webResp));
            else
            {
                string html = Encoding.ASCII.GetString(webResp);

                const string startKey = "serv_id=";
                const string endKey = "')";

                //var results = new List<string>();
                int index = 0;

                while (true)
                {
                    // Find "serv_id="
                    int start = html.IndexOf(startKey, index, StringComparison.OrdinalIgnoreCase);
                    if (start == -1)
                        break; // no more matches

                    start += startKey.Length;

                    // Find "')"
                    int end = html.IndexOf(endKey, start, StringComparison.Ordinal);
                    if (end == -1)
                        break;

                    string value = html.Substring(start, end - start);

                    if ( !scheds.Exists(s => s == value) )  // skip any duplicate SCHEDA entries, such as with WAVs
                        scheds.Add(value);

                    index = end + endKey.Length;
                }
            }
            return success;
        }

        //perform NTLM authentication for MegaTool with users LAN ID
        public static bool Auth(string user, string pass, ref CookieContainer cookies)
        {
            string url = "http://megatool.rogers.com/megatool/megatool/";
            bool success = false;

            // Explicit credentials (domain\user)
            // var creds = new NetworkCredential(user, pass, "RCI");

            // Add to global cred cache for NTLM auth - Needed to survive Windows 11 / .NET481.  Works fine in Mono or with Fiddler, but not straight .exe
            bdf.cache.Add(new Uri(url), "NTLM", CredentialCache.DefaultNetworkCredentials);    

            // A cookie container is REQUIRED to capture session cookies

            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.AllowAutoRedirect = false;          // Required for NTLM handshake
            request.PreAuthenticate = true;            
            //request.PreAuthenticate = false;            // NTLM cannot pre-authenticate
            request.UseDefaultCredentials = false;
            request.Credentials = bdf.cache;
            //request.Credentials = creds;                // Enables SSPI NTLM handshake
            request.CookieContainer = cookies;          // Store session cookies
            request.KeepAlive = true;                   // NTLM requires same connection
            //request.UnsafeAuthenticatedConnectionSharing = true;
            request.UserAgent = "NTLMClient/.NET4.8";

            Logger.Log(1, "Megatool authentication for ({0})...", user);

            HttpWebResponse response;

            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException wex) when (wex.Response is HttpWebResponse)
            {
                response = (HttpWebResponse)wex.Response;
            }

            // If server demands NTLM, the framework automatically:
            //   • sends Type1
            //   • receives Type2
            //   • sends Type3
            //   • then returns the real authenticated page response

            Logger.Log(5, "HTTP Status: " + (int)response.StatusCode + " " + response.StatusCode);

            // Read response body
            string body;
            using (var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                body = reader.ReadToEnd();
            }

            foreach (Cookie ck in cookies.GetCookies(request.RequestUri))
            {
                Logger.Log(5, $"{ck.Name} = {ck.Value}; Domain={ck.Domain}; Path={ck.Path}");
                success = true;
            }

            response.Close();

            return success;
        }

    }
}