using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using Common.Constant;
using log4net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.Net;

namespace Common.Logic;

public class CommonLogic
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CommonLogic));

    public static String GenToken(IConfiguration configuration, String Email, String accountId)
    {
        var issuer = configuration["Jwt:Issuer"];
        var audience = configuration["Jwt:Audience"];
        var key = Encoding.ASCII.GetBytes(configuration["Jwt:Key"]!);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(
                new[]
                {
                    new Claim("Id", Guid.NewGuid().ToString()),
                    new Claim("Msisdn", Email),
                    new Claim("AccountId", accountId),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                }
            ),
            Expires = DateTime.UtcNow.AddMinutes(int.Parse(configuration["Jwt:Time"] ?? "900")),
            Issuer = issuer,
            Audience = audience,
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key),
                SecurityAlgorithms.HmacSha512Signature
            )
        };
        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);
        var stringToken = tokenHandler.WriteToken(token);
        return stringToken;
    }

    public static string GenRefreshToken(IConfiguration configuration, string msisdn)
    {
        var issuer = configuration["Jwt:Issuer"];
        var audience = configuration["Jwt:Audience"];
        var key = Encoding.ASCII.GetBytes(configuration["Jwt:Key"]);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(
                new[]
                {
                    new Claim("Id", Guid.NewGuid().ToString()),
                    new Claim("Msisdn", msisdn),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                }
            ),
            Expires = DateTime.UtcNow.AddMinutes(int.Parse(configuration["Jwt:Time"] ?? "900")),
            Issuer = issuer,
            Audience = audience,
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key),
                SecurityAlgorithms.HmacSha512Signature
            )
        };
        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);
        var stringToken = tokenHandler.WriteToken(token);
        return stringToken;
    }

    public static string? GetDataFromToken(
        IConfiguration configuration,
        HttpRequest httpRequest,
        string nameKey
    )
    {
        string realToken = "";
        var token = httpRequest.Headers["Authorization"];
        if (token.Count == 0)
        {
            return null;
        }
        realToken = token[0];

        // Trim 'Bearer ' from the start since its just a prefix for the token
        var jwtEncodedString = realToken.Substring(7);

        string? value = GetPath(configuration, jwtEncodedString, nameKey);
        if (value == null)
        {
            return null;
        }
        string url = httpRequest.Path;
        log.Info("url: " + url + " || nameKey: " + nameKey + " value: " + value);
        return value;
    }

    public static string? GetPath(IConfiguration configuration, string token, string nameKey)
    {
        var key = Encoding.ASCII.GetBytes(configuration["Jwt:Key"] ?? "");
        var handler = new JwtSecurityTokenHandler();
        var validations = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateLifetime = true
        };
        var claims = handler.ValidateToken(token, validations, out var tokenSecure);
        var data = claims.Claims.First(x => x.Type == nameKey).Value;
        return data;
    }

    public static string? ValidateMsisdn(string? input)
    {
        if (input == null || input.Length == 0 || !long.TryParse(input, out long temp))
        {
            return null;
        }
        else if (input.StartsWith("255") && input.Length == 12)
        {
            return input.Trim();
        }
        else if (input.StartsWith("0") && input.Length == 10)
        {
            input = "255" + input.Substring(1);
            return input.Trim();
        }
        return null;
    }

    public static string GenPassword(int length)
    {
        const string chars = "0123456789";
        var random = new Random();
        var password = new StringBuilder();
        for (int i = 0; i < length; i++)
        {
            password.Append(chars[random.Next(chars.Length)]);
        }
        return password.ToString();
    }

    /// <summary>
    /// Get language from request header or body
    /// Priority: header Lang > header Accept-Language > body lang > default "lo"
    /// </summary>
    /// <param name="httpRequest">HTTP request</param>
    /// <param name="bodyLang">Language from request body (optional)</param>
    /// <returns>Language code: "lo" or "en"</returns>
    public static string GetLanguage(HttpRequest httpRequest, string? bodyLang = null)
    {
        // Check custom Lang header first (highest priority)
        var langHeader = httpRequest.Headers["Lang"].FirstOrDefault();
        if (!string.IsNullOrEmpty(langHeader))
        {
            var lang = langHeader.ToLower();
            if (lang == "en" || lang == "lo")
                return lang;
        }

        // Check Accept-Language header
        var acceptLangHeader = httpRequest.Headers["Accept-Language"].FirstOrDefault();
        if (!string.IsNullOrEmpty(acceptLangHeader))
        {
            // Accept-Language can be "en", "lo", "en-US,en;q=0.9", etc.
            var lang = acceptLangHeader.Split(',')[0].Split('-')[0].ToLower();
            if (lang == "en" || lang == "lo")
                return lang;
        }

        // Then check body
        if (!string.IsNullOrEmpty(bodyLang))
            return bodyLang.ToLower();

        return "lo"; // Default
    }

    public static string ConvertIntStatus(int? status)
    {
        switch (status)
        {
            case CommonConstant.StatusActive:
                return "Active";
            case CommonConstant.StatusInactive:
                return "Inactive";
            case CommonConstant.StatusClaimed:
                return "Claimed";
            case CommonConstant.StatusNotClaimed:
                return "Not Claimed";
            default:
                return "All";
        }
    }

    public static DateTime ConvertStringToTime(string? time)
    {
        return DateTime.ParseExact(
            time!,
            CommonConstant.ListDateTimeFormat,
            System.Globalization.CultureInfo.InvariantCulture,
            DateTimeStyles.None
        );
    }

    public static string GetClassNameByIntStatus(int status)
    {
        switch (status)
        {
            case CommonConstant.StatusActive:
                return "bg-success";
            case CommonConstant.StatusClaimed:
                return "bg-success";
            case CommonConstant.StatusNotClaimed:
                return "bg-danger";
            case CommonConstant.StatusInactive:
                return "bg-black";
            default:
                return "bg-success";
        }
    }

    public static string? SendPostWithAuthen(ILog log, string Url, dynamic data, string authenToken,string lang = "tu")
    {
        var json = JsonConvert.SerializeObject(data);
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
        request.Method = "POST";
        request.ContentType = "application/json";
        request.ContentLength = json.Length;
        request.Headers.Add("Authorization", "Bearer " + authenToken);
        request.Headers.Add("Accept-Language", lang);
        log.Info("request: " + Url + " data: " + json);

        using (Stream webStream = request.GetRequestStream())
        using (StreamWriter requestWriter = new StreamWriter(webStream, System.Text.Encoding.ASCII))
        {
            requestWriter.Write(json);
        }
        try
        {
            WebResponse webResponse = request.GetResponse();
            using (Stream webStream = webResponse.GetResponseStream() ?? Stream.Null)
            using (StreamReader responseReader = new StreamReader(webStream))
            {
                string response = responseReader.ReadToEnd();
                log.Info("request: " + Url + " /nData: " + json + " /n response: " + response);
                return response;
            }
        }
        catch (Exception e)
        {
            log.Info("-----------------");
            log.Info(e.Message);
        }
        return null;
    }

    public static string? SendPost(ILog log, string Url, dynamic data)
    {
        var json = JsonConvert.SerializeObject(data);

        log.Info("request: " + Url + " data: " + json);

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
        request.Method = "POST";
        request.ContentType = "application/json";
        request.ContentLength = json.Length;

        // CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
        // log.Debug("Lang: " + currentCulture.Name);
        // request.Headers.Add("Lang", currentCulture.Name);

        using (Stream webStream = request.GetRequestStream())
        using (StreamWriter requestWriter = new StreamWriter(webStream, System.Text.Encoding.ASCII))
        {
            requestWriter.Write(json);
        }
        try
        {
            WebResponse webResponse = request.GetResponse();
            using (Stream webStream = webResponse.GetResponseStream() ?? Stream.Null)
            using (StreamReader responseReader = new StreamReader(webStream))
            {
                string response = responseReader.ReadToEnd();
                log.Info("request: " + Url + " /nData: " + json + " /n response: " + response);
                return response;
            }
        }
        catch (Exception e)
        {
            log.Info("-----------------");
            log.Info(e.Message);
        }
        return null;
    }

    public static string GetErrorDescription(string code)
    {
        switch (code)
        {
            case "0":
                return "SUBSCRIBER put to queue Success";
            case "1":
                return "SUBSCRIBER put to Queue failed";
            case "2":
                return "Unauthentic";
            case "15":
                return "Missing value (Input params null) / Invalid Parameter(s)";
            case "200":
                return "Invalid username or password";
            case "201":
                return "Invalid Request Type";
            case "202":
                return "Invalid Request";
            case "203":
                return "Invalid package";
            case "204":
                return "Server too busy";
            case "400":
                return "Not register";
            case "401":
                return "Not enough money";
            case "403":
                return "Already register";
            case "408":
                return "Get price charge error";
            case "411":
                return "Subscriber was cancel service";
            case "412":
                return "Subscriber not use service";
            case "413":
                return "Conflict package. Subscriber is using other package in same service";
            case "418":
                return "MO command not support";
            case "419":
                return "3rd Party is incorrect";
            case "420":
                return "Wrong format datetime: yyyy-MM-dd";
            case "504":
                return "Unknown error";
            default:
                return "Unknown error code";
        }
    }

    //public static EnvelopeNoNs ParseSoapResponse(string xml)
    //{
    //    // Bỏ namespace để dễ deserialize
    //    XDocument doc = XDocument.Parse(xml);
    //    foreach (var elem in doc.Descendants())
    //        elem.Name = elem.Name.LocalName;

    //    string cleanXml = doc.ToString();

    //    var serializer = new XmlSerializer(typeof(EnvelopeNoNs));
    //    using var reader = new StringReader(cleanXml);
    //    return (EnvelopeNoNs)serializer.Deserialize(reader)!;
    //}
}
