using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Linq;

namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
    /// <summary>
    /// Service for managing orders in InnVoice API
    /// </summary>
    public class InnVoiceOrderService
    {
        private readonly HttpClient _httpClient;
        private readonly string _companyName;
        private readonly string _username;
        private readonly string _password;
        private readonly string _baseUrl;

        //public InnVoiceOrderService(string companyName = "apiteszt", string username = "apiteszt", string password = "dsjfluio4324hjhjfdhkjskjh213kjgsd", string baseUrl = "https://api.innvoice.hu")
        public InnVoiceOrderService(string companyName = "fruitbank", string username = "fruitbank", string password = "YkKzM1dwJax0HTlPWlMABSqdSA", string baseUrl = "https://api.innvoice.hu")
        {
            _companyName = companyName ?? throw new ArgumentNullException(nameof(companyName));
            _username = username ?? throw new ArgumentNullException(nameof(username));
            _password = password ?? throw new ArgumentNullException(nameof(password));
            _baseUrl = baseUrl.TrimEnd('/');

            _httpClient = new HttpClient();
            SetupAuthentication();
        }

        private void SetupAuthentication()
        {
            var authToken = Convert.ToBase64String(
                System.Text.Encoding.ASCII.GetBytes($"{_username}:{_password}")
            );
            _httpClient.DefaultRequestHeaders.Authorization =
                new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authToken);
        }

        /// <summary>
        /// Create one or more orders
        /// </summary>
        public async Task<List<OrderCreateResponse>?> CreateOrdersAsync(List<OrderCreateRequest> orders)
        {
            var url = $"{_baseUrl}/{_companyName}/order";

            var xml = BuildOrdersXml(orders);
            var content = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("data", xml)
            });

            try
            {
                var response = await _httpClient.PostAsync(url, content);
                Console.WriteLine($"InnVoice API CreateOrdersAsync response status: {response.StatusCode}");
                response.EnsureSuccessStatusCode();
                
                var responseContent = await response.Content.ReadAsStringAsync();
                return ParseOrderResponses(responseContent);
            }
            catch (HttpRequestException ex)
            {
                throw new InnVoiceApiException($"Error creating orders: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// Create a single order
        /// </summary>
        public async Task<OrderCreateResponse> CreateOrderAsync(OrderCreateRequest order)
        {
            var responses = await CreateOrdersAsync(new List<OrderCreateRequest> { order });
            if (responses != null && responses.Count != 1) throw new Exception($"InvoiceOrderApi responses.Count != 1!");

            return responses?.FirstOrDefault() ?? new OrderCreateResponse();
        }

        
        /// <summary>
        /// Get orders modified since a specific timestamp
        /// Format: YYYYMMDDHHmmss (year, month, day, hour, minute, second)
        /// Recommended for tracking changes
        /// Rate limit: Full queries or queries older than current month limited to 10 times per 30 days
        /// Recommended: Only use current month dates
        /// </summary>
        public async Task<List<InnVoiceOrder>> GetOrdersByUpdateTimeAsync(DateTime updateTime)
        {
            var timeStr = updateTime.ToString("yyyyMMddHHmmss");
            var url = $"{_baseUrl}/{_companyName}/order/updatedtime/{timeStr}";
            var result = await GetOrdersFromUrlAsync(url);
            return result;
        }

        public async Task<List<InnVoiceOrder>> GetOrdersByOrderDateAsync(DateTime orderDate)
        {
            var timeStr = orderDate.ToString("yyyy.MM.dd.");  // Format: éééé.hh.nn.
            var url = $"{_baseUrl}/{_companyName}/order/megrendeleskelte/{timeStr}";
            Console.WriteLine($"DEBUG InnVoiceOrderService: Calling URL: {url}");
            var result = await GetOrdersFromUrlAsync(url);
            return result;
        }

        private string BuildOrdersXml(List<OrderCreateRequest> orders)
        {
            var ordersElement = new XElement("orders");

            foreach (var order in orders)
            {
                var orderElement = new XElement("order");

                orderElement.Add(new XElement("VevoNev", new XCData(order.VevoNev ?? "")));
                orderElement.Add(new XElement("VevoIrsz", new XCData(order.VevoIrsz ?? "")));
                orderElement.Add(new XElement("VevoTelep", new XCData(order.VevoTelep ?? "")));
                orderElement.Add(new XElement("VevoUtcaHsz", new XCData(order.VevoUtcaHsz ?? "")));

                if (!string.IsNullOrEmpty(order.VevoOrszag))
                    orderElement.Add(new XElement("VevoOrszag", new XCData(order.VevoOrszag)));

                if (!string.IsNullOrEmpty(order.VevoAdoszam))
                    orderElement.Add(new XElement("VevoAdoszam", new XCData(order.VevoAdoszam)));

                if (!string.IsNullOrEmpty(order.SzallNev))
                    orderElement.Add(new XElement("SzallNev", new XCData(order.SzallNev)));
                if (!string.IsNullOrEmpty(order.SzallIrsz))
                    orderElement.Add(new XElement("SzallIrsz", new XCData(order.SzallIrsz)));
                if (!string.IsNullOrEmpty(order.SzallTelep))
                    orderElement.Add(new XElement("SzallTelep", new XCData(order.SzallTelep)));
                if (!string.IsNullOrEmpty(order.SzallUtcaHsz))
                    orderElement.Add(new XElement("SzallUtcaHsz", new XCData(order.SzallUtcaHsz)));
                if (!string.IsNullOrEmpty(order.SzallOrszag))
                    orderElement.Add(new XElement("SzallOrszag", new XCData(order.SzallOrszag)));

                orderElement.Add(new XElement("MegrendelestombID", new XCData(order.MegrendelestombID.ToString())));
                orderElement.Add(new XElement("MegrendelesKelte", new XCData(order.MegrendelesKelte.ToString("yyyy.MM.dd."))));
                orderElement.Add(new XElement("Hatarido", new XCData(order.Hatarido.ToString("yyyy.MM.dd."))));
                orderElement.Add(new XElement("Devizanem", new XCData(order.Devizanem ?? "")));

                if (!string.IsNullOrEmpty(order.FizetesiMod))
                    orderElement.Add(new XElement("FizetesiMod", new XCData(order.FizetesiMod)));
                if (!string.IsNullOrEmpty(order.Megjegyzes))
                    orderElement.Add(new XElement("Megjegyzes", new XCData(order.Megjegyzes)));
                if (!string.IsNullOrEmpty(order.Email))
                    orderElement.Add(new XElement("Email", new XCData(order.Email)));
                if (!string.IsNullOrEmpty(order.Telefon))
                    orderElement.Add(new XElement("Telefon", new XCData(order.Telefon)));
                if (!string.IsNullOrEmpty(order.MegrendelesSzamStr))
                    orderElement.Add(new XElement("MegrendelesSzamStr", new XCData(order.MegrendelesSzamStr)));

                // Add items
                foreach (var item in order.Items)
                {
                    var tetelElement = new XElement("tetel");
                    tetelElement.Add(new XElement("TetelNev", item.TetelNev ?? ""));
                    tetelElement.Add(new XElement("AfaSzoveg", item.AfaSzoveg ?? ""));
                    tetelElement.Add(new XElement("Brutto", item.Brutto ? "1" : "0"));
                    tetelElement.Add(new XElement("EgysegAr", item.EgysegAr.ToString("0.##", CultureInfo.InvariantCulture)));
                    tetelElement.Add(new XElement("Mennyiseg", item.Mennyiseg.ToString("0.##", CultureInfo.InvariantCulture)));
                    tetelElement.Add(new XElement("MennyisegEgyseg", new XCData(item.MennyisegEgyseg ?? "")));

                    if (!string.IsNullOrEmpty(item.CikkSzam))
                        tetelElement.Add(new XElement("CikkSzam", new XCData(item.CikkSzam)));

                    orderElement.Add(tetelElement);
                }

                ordersElement.Add(orderElement);
            }

            return new XDeclaration("1.0", "UTF-8", null).ToString() + "\n" + ordersElement.ToString();
        }

        private List<OrderCreateResponse>? ParseOrderResponses(string xml)
        {
            var responses = new List<OrderCreateResponse>();

            try
            {
                var doc = XDocument.Parse(xml);
                var orderElements = doc.Descendants("order");

                foreach (var orderElement in orderElements)
                {
                    var response = new OrderCreateResponse
                    {
                        ErrorCode = orderElement.Element("error")?.Value?.Trim(),
                        Message = orderElement.Element("message")?.Value?.Trim(),
                        TableId = int.TryParse(orderElement.Element("TABLE_ID")?.Value?.Trim(), out var tid) ? tid : (int?)null,
                        TechId = orderElement.Element("techid")?.Value?.Trim(),
                        PrintUrl = orderElement.Element("PrintUrl")?.Value?.Trim()
                    };

                    responses.Add(response);
                }
            }
            catch (Exception ex)
            {
                throw new InnVoiceApiException($"Error parsing order response XML: {ex.Message}", ex);
            }

            return responses.Count == 0 ? null : responses;
        }

        /// <summary>
        /// Get order by technical ID
        /// </summary>
        public async Task<List<InnVoiceOrder>> GetOrderByTechIdAsync(string techId)
        {
            var url = $"{_baseUrl}/{_companyName}/order/techid/{Uri.EscapeDataString(techId)}";
            return await GetOrdersFromUrlAsync(url);
        }

        /// <summary>
        /// Get order by table ID
        /// </summary>
        public async Task<InnVoiceOrder> GetOrderByIdAsync(int tableId)
        {
            var url = $"{_baseUrl}/{_companyName}/order/id/{tableId}";
            var orders = await GetOrdersFromUrlAsync(url);
            return orders.Count > 0 ? orders[0] : null;
        }

        private async Task<List<InnVoiceOrder>> GetOrdersFromUrlAsync(string url)
        {
            try
            {
                var response = await _httpClient.GetAsync(url);
                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();

                // Parse XML response
                return ParseOrdersFromXml(content);
            }
            catch (HttpRequestException ex)
            {
                throw new InnVoiceApiException($"Error calling InnVoice API: {ex.Message}", ex);
            }
            catch (Exception ex)
            {
                throw new InnVoiceApiException($"Error parsing API response: {ex.Message}", ex);
            }
        }

        private List<InnVoiceOrder> ParseOrdersFromXml(string xml)
        {
            var orders = new List<InnVoiceOrder>();

            try
            {
                var doc = XDocument.Parse(xml);
                var orderElements = doc.Descendants("order");

                foreach (var orderElement in orderElements)
                {
                    var order = new InnVoiceOrder
                    {
                        TableId = GetIntValue(orderElement, "TABLE_ID"),
                        VevoID = GetIntValue(orderElement, "VevoID"),
                        CustomerName = GetStringValue(orderElement, "VevoNev"),
                        CustomerZipCode = GetStringValue(orderElement, "VevoIrsz"),
                        CustomerCity = GetStringValue(orderElement, "VevoTelep"),
                        CustomerAddress = GetStringValue(orderElement, "VevoUtcaHsz"),
                        CustomerCountry = GetStringValue(orderElement, "VevoOrszag"),
                        CustomerTaxNumber = GetStringValue(orderElement, "VevoAdoszam"),
                        ShippingName = GetStringValue(orderElement, "SzallNev"),
                        ShippingZipCode = GetStringValue(orderElement, "SzallIrsz"),
                        ShippingCity = GetStringValue(orderElement, "SzallTelep"),
                        ShippingAddress = GetStringValue(orderElement, "SzallUtcaHsz"),
                        ShippingCountry = GetStringValue(orderElement, "SzallOrszag"),
                        OrderBookId = GetIntValue(orderElement, "MegrendelestombID"),
                        OrderDate = GetStringValue(orderElement, "MegrendelesKelte"),
                        DueDate = GetStringValue(orderElement, "Hatarido"),
                        Currency = GetStringValue(orderElement, "Devizanem"),
                        PaymentMethod = GetStringValue(orderElement, "FizetesiMod"),
                        Notes = GetStringValue(orderElement, "Megjegyzes"),
                        Email = GetStringValue(orderElement, "Email"),
                        Phone = GetStringValue(orderElement, "Telefon"),
                        ExternalOrderNumber = GetStringValue(orderElement, "MegrendelesSzamStr"),
                        TotalNet = GetDecimalValue(orderElement, "NettoErtek"),
                        TotalVAT = GetDecimalValue(orderElement, "AFAErtek"),
                        TotalGross = GetDecimalValue(orderElement, "BruttoErtek"),
                        TechId = GetStringValue(orderElement, "techid"),
                        PrintUrl = GetStringValue(orderElement, "PrintLink"),
                        UpdateTime = GetStringValue(orderElement, "_UpdateTime"),
                        QueryTime = GetStringValue(orderElement, "QueryTime")
                    };

                    // Parse line items
                    var itemElements = orderElement.Descendants("tetel");
                    foreach (var itemElement in itemElements)
                    {
                        order.Items.Add(new InnVoiceOrderLineItem
                        {
                            TableId = GetIntValue(itemElement, "TABLE_ID"),
                            ItemName = GetStringValue(itemElement, "TetelNev"),
                            ArticleNumber = GetStringValue(itemElement, "CikkSzam"),
                            VATText = GetStringValue(itemElement, "AfaSzoveg"),
                            VATRate = GetDecimalValue(itemElement, "AfaKulcs"),
                            IsGross = GetIntValue(itemElement, "Brutto") == 1,
                            UnitPrice = GetDecimalValue(itemElement, "EgysegAr"),
                            Quantity = GetDecimalValue(itemElement, "Mennyiseg"),
                            Unit = GetStringValue(itemElement, "MennyisegEgyseg")
                        });
                    }

                    orders.Add(order);
                }
            }
            catch (Exception ex)
            {
                throw new InnVoiceApiException($"Error parsing XML: {ex.Message}", ex);
            }

            return orders;
        }

        private string GetStringValue(XElement element, string name)
        {
            return element.Element(name)?.Value?.Trim() ?? string.Empty;
        }

        private int GetIntValue(XElement element, string name)
        {
            var value = GetStringValue(element, name);
            return int.TryParse(value, out var result) ? result : 0;
        }

        private decimal GetDecimalValue(XElement element, string name)
        {
            var value = GetStringValue(element, name);
            return decimal.TryParse(value, out var result) ? result : 0m;
        }
    }

    // Order Models
    public class OrderCreateRequest
    {
        public string VevoNev { get; set; }
        public string VevoIrsz { get; set; }
        public string VevoTelep { get; set; }
        public string VevoUtcaHsz { get; set; }
        public string VevoOrszag { get; set; }
        public string VevoAdoszam { get; set; }
        public string SzallNev { get; set; }
        public string SzallIrsz { get; set; }
        public string SzallTelep { get; set; }
        public string SzallUtcaHsz { get; set; }
        public string SzallOrszag { get; set; }
        public int MegrendelestombID { get; set; }
        public DateTime MegrendelesKelte { get; set; }
        public DateTime Hatarido { get; set; }
        public string Devizanem { get; set; }
        public string FizetesiMod { get; set; }
        public string Megjegyzes { get; set; }
        public string Email { get; set; }
        public string Telefon { get; set; }
        public string MegrendelesSzamStr { get; set; }

        public List<InnVoiceOrderItem> Items { get; set; } = new List<InnVoiceOrderItem>();

        public void AddItem(InnVoiceOrderItem item)
        {
            Items.Add(item);
        }
    }

    public class InnVoiceOrderItem
    {
        public string TetelNev { get; set; }
        public string AfaSzoveg { get; set; }
        public bool Brutto { get; set; }
        public decimal EgysegAr { get; set; }
        public decimal Mennyiseg { get; set; }
        public string MennyisegEgyseg { get; set; }
        public string CikkSzam { get; set; }
    }

    public class OrderCreateResponse
    {
        public string ErrorCode { get; set; }
        public string Message { get; set; }
        public int? TableId { get; set; }
        public string TechId { get; set; }
        public string PrintUrl { get; set; }

        public bool IsSuccess => ErrorCode == "200";
    }

    public class InnVoiceApiException : Exception
    {
        public InnVoiceApiException(string message) : base(message) { }
        public InnVoiceApiException(string message, Exception innerException) : base(message, innerException) { }
    }

    // Order GET response models
    public class InnVoiceOrder
    {
        public int TableId { get; set; }
        public int VevoID { get; set; }
        public string CustomerName { get; set; }
        public string CustomerZipCode { get; set; }
        public string CustomerCity { get; set; }
        public string CustomerAddress { get; set; }
        public string CustomerCountry { get; set; }
        public string CustomerTaxNumber { get; set; }
        public string ShippingName { get; set; }
        public string ShippingZipCode { get; set; }
        public string ShippingCity { get; set; }
        public string ShippingAddress { get; set; }
        public string ShippingCountry { get; set; }
        public int OrderBookId { get; set; }
        public string OrderDate { get; set; }
        public string DueDate { get; set; }
        public string Currency { get; set; }
        public string PaymentMethod { get; set; }
        public string Notes { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public string ExternalOrderNumber { get; set; }
        public decimal TotalNet { get; set; }
        public decimal TotalVAT { get; set; }
        public decimal TotalGross { get; set; }
        public string TechId { get; set; }
        public string PrintUrl { get; set; }
        public string UpdateTime { get; set; }
        public string QueryTime { get; set; }

        public List<InnVoiceOrderLineItem> Items { get; set; } = new List<InnVoiceOrderLineItem>();
    }

    public class InnVoiceOrderLineItem
    {
        public int TableId { get; set; }
        public string ItemName { get; set; }
        public string ArticleNumber { get; set; }
        public string VATText { get; set; }
        public decimal VATRate { get; set; }
        public bool IsGross { get; set; }
        public decimal UnitPrice { get; set; }
        public decimal Quantity { get; set; }
        public string Unit { get; set; }
    }
}