using IWS5.Importer.Abstractions.Interfaces;
using Serilog.Context;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using ILogger = Serilog.ILogger;

namespace IWS5.Importer.Worker.Xml;

public class XmlParser : IXmlParser
{
    private readonly List<string> _xmlSections;
    private readonly ILogger _logService;

    public XmlParser(ILogger logService)
    {
        _logService = logService;
        _xmlSections = new List<string> { "PROCESS", "FOLDER", "DOCUMENT" };
    }

    /// <summary>
    /// Parses the XML file and updates the existing dictionary with data from the specified sections.
    /// </summary>
    /// <param name="xmlFilePath">Path to the XML file.</param>
    /// <param name="existingData">Existing dictionary to merge the parsed XML data into.</param>
    public Dictionary<string, Dictionary<string, string>> ParseAndUpdateXml(
        string xmlFilePath,
        Dictionary<string, Dictionary<string, string>> existingData, bool warningLoginOfMissedSectionInXML = false)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(xmlFilePath) || !File.Exists(xmlFilePath))
            {
                LogError($"XML file not found or invalid: {xmlFilePath}");
                return existingData;
            }

            XDocument xmlDoc;

            try
            {
                try
                {
                    using (var reader = new StreamReader(xmlFilePath, Encoding.UTF8, detectEncodingFromByteOrderMarks: true))
                    {
                        xmlDoc = XDocument.Load(reader);
                    }
                }
                catch (XmlException)
                {
                    // Retry with Windows-1251
                    using (var reader = new StreamReader(xmlFilePath, Encoding.GetEncoding("windows-1251")))
                    {
                        xmlDoc = XDocument.Load(reader);
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"Failed to load XML document: {xmlFilePath}", ex);
                return existingData;
            }

            if (xmlDoc.Root == null)
            {
                LogError($"XML file '{xmlFilePath}' has no root element.");
                return existingData;
            }

            // Process sections specified in the configuration
            foreach (var section in _xmlSections)
            {
                var sectionElement = xmlDoc.Root.Element(section);
                if (sectionElement == null) continue;

                var sectionValues = ExtractFields(sectionElement);

                if (sectionValues.Count == 0) continue;

                if (existingData.TryGetValue(section, out var existingSection))
                {
                    foreach (var item in sectionValues)
                    {
                        existingSection[item.Key] = item.Value;
                    }
                }
                else
                {
                    existingData[section] = sectionValues;
                }
            }

            if (warningLoginOfMissedSectionInXML)
            {
                foreach (var section in _xmlSections)
                {
                    if (xmlDoc.Root.Element(section) == null)
                    {
                        using (LogContext.PushProperty("LogContext", nameof(XmlParser)))
                            _logService.Warning($"Section '{section}' not found in XML file: {xmlFilePath}");
                    }
                }
            }

            using (LogContext.PushProperty("LogContext", nameof(XmlParser)))
                _logService.Information("XML {XmlFilePath} processed successfully!", xmlFilePath);
        }
        catch (Exception ex)
        {
            using (LogContext.PushProperty("LogContext", nameof(XmlParser)))
                _logService.Error(ex, "Error parsing XML {XmlFilePath}", xmlFilePath);
        }

        return existingData;
    }

    private static Dictionary<string, string> ExtractFields(XElement sectionElement)
    {
        return sectionElement.Descendants()
                            .Where(e => !e.HasElements)
                            .ToDictionary(e => e.Name.LocalName, e => e.Value.Trim());
    }

    private void LogError(string message, Exception? ex = null)
    {
        using (LogContext.PushProperty("LogContext", nameof(XmlParser)))
            _logService.Error(ex, messageTemplate: message);
    }
}
