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

namespace IWS5.Importer.Worker.Xml;

public class XmlValidator : IXmlValidator
{
    private readonly ILogger _logService;
    private readonly IFileMover _fileMover;
    private readonly Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<string, bool>>>> _cachedValidationRules;

    public XmlValidator(ILogger logService, IFileMover fileMover)
    {
        _logService = logService;
        _fileMover = fileMover;
        _cachedValidationRules = new Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<string, bool>>>>();
    }

    /// This code could be used in the future if needed (addditon feature of validation xml rule)
    /// for each level (folder, document...) of xml
    /// https://greenfieldinvestments.atlassian.net/wiki/x/AQDOCg
    public bool ValidateXml(string? xmlFilePath, string validationLevel)
    {
        if (xmlFilePath is null || string.IsNullOrEmpty(xmlFilePath))
            return true;

        if (!_cachedValidationRules.TryGetValue(validationLevel, out var levelRules))
        {
            using (LogContext.PushProperty("LogContext", nameof(XmlValidator)))
                _logService.Warning("No validation rules found for level: {Level}", validationLevel);
            return true;
        }

        XmlDocument xmlDoc = new XmlDocument();

        try
        {
            using (var reader = new StreamReader(xmlFilePath, Encoding.UTF8, detectEncodingFromByteOrderMarks: true))
            {
                xmlDoc.Load(reader);
            }
        }
        catch (Exception ex)
        {
            HandleValidationFailure(xmlFilePath, $"Failed to load XML document: {ex.Message}");
            return false;
        }

        foreach (var section in levelRules)
        {
            XmlNode? xmlSection = xmlDoc.SelectSingleNode($"/ROOT/{section.Key}");
            if (xmlSection == null)
            {
                return HandleValidationFailure(xmlFilePath, $"Missing section: {section.Key}");
            }

            foreach (var element in section.Value)
            {
                XmlNode? node;

                if (section.Key == XmlSectionKeys.Process)
                {
                    node = xmlSection.SelectSingleNode(element.Key);
                }
                else
                {
                    var tempNode = xmlSection.SelectSingleNode($"FIELDS/{element.Key}");
                    node = tempNode == null ? xmlSection.SelectSingleNode(element.Key) : tempNode;
                }

                if (element.Value["isRequired"] && (node == null || string.IsNullOrWhiteSpace(node.InnerText)))
                {
                    return HandleValidationFailure(xmlFilePath, $"Missing required element: {element.Key} in section {section.Key}");
                }

                if (element.Value.ContainsKey("isNumber") && element.Value["isNumber"] && !int.TryParse(node?.InnerText, out _))
                {
                    return HandleValidationFailure(xmlFilePath, $"Element {element.Key} must be a number in {xmlFilePath}");
                }
            }
        }
        using (LogContext.PushProperty("LogContext", nameof(XmlValidator)))
            _logService.Information("XML validation passed: {FilePath}", xmlFilePath);
        return true;
    }

    private bool HandleValidationFailure(string filePath, string reason)
    {
        using (LogContext.PushProperty("LogContext", nameof(XmlValidator)))
            _logService.Error(reason + " | File: {FilePath}", filePath);
        return false;
    }
}
