using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Haoliang.Models.Template;
using Haoliang.Models.Device;
using Haoliang.Models.DataCollection;
using Haoliang.Data.Repositories;

namespace Haoliang.Core.Services
{
    public interface ITemplateValidationService
    {
        Task<bool> ValidateTemplateStructureAsync(CNCBrandTemplate template);
        Task<bool> ValidateTagMappingsAsync(CNCBrandTemplate template);
        Task<bool> ValidateDataParsingRulesAsync(CNCBrandTemplate template);
        Task<IEnumerable<ValidationError>> ValidateTemplateForDeviceAsync(int templateId, int deviceId);
        Task<bool> TestTemplateDataParsingAsync(CNCBrandTemplate template, string sampleData);
        Task<IEnumerable<string>> GetMissingRequiredTagsAsync(CNCBrandTemplate template);
        Task<ValidationReport> ValidateTemplateComprehensivelyAsync(CNCBrandTemplate template);
        Task<IEnumerable<TagValidationResult>> ValidateDeviceDataAsync(IEnumerable<TagData> deviceTags, int templateId);
        Task<bool> ValidateTemplateCompatibilityAsync(CNCBrandTemplate template1, CNCBrandTemplate template2);
    }

    public class TemplateValidationService : ITemplateValidationService
    {
        private readonly ITemplateRepository _templateRepository;
        private readonly ITagMappingRepository _tagMappingRepository;
        private readonly ILoggingService _loggingService;

        public TemplateValidationService(
            ITemplateRepository templateRepository,
            ITagMappingRepository tagMappingRepository,
            ILoggingService loggingService)
        {
            _templateRepository = templateRepository;
            _tagMappingRepository = tagMappingRepository;
            _loggingService = loggingService;
        }

        public async Task<bool> ValidateTemplateStructureAsync(CNCBrandTemplate template)
        {
            var errors = new List<ValidationError>();

            // Check basic structure
            if (template == null)
            {
                errors.Add(new ValidationError { Field = "template", Message = "Template cannot be null" });
                return false;
            }

            if (string.IsNullOrWhiteSpace(template.TemplateName))
            {
                errors.Add(new ValidationError { Field = "templateName", Message = "Template name is required" });
            }

            if (string.IsNullOrWhiteSpace(template.BrandName))
            {
                errors.Add(new ValidationError { Field = "brandName", Message = "Brand name is required" });
            }

            if (template.Tags == null || !template.Tags.Any())
            {
                errors.Add(new ValidationError { Field = "tags", Message = "At least one tag must be defined" });
            }

            // Validate tag structure
            if (template.Tags != null)
            {
                for (int i = 0; i < template.Tags.Count; i++)
                {
                    var tag = template.Tags[i];
                    var tagErrors = ValidateTagStructure(tag, $"tags[{i}]");
                    errors.AddRange(tagErrors);
                }
            }

            // Validate data processing rules
            if (template.DataProcessingRules != null)
            {
                foreach (var rule in template.DataProcessingRules)
                {
                    var ruleErrors = ValidateDataProcessingRule(rule);
                    errors.AddRange(ruleErrors);
                }
            }

            // Log validation results
            if (errors.Any())
            {
                await _loggingService.LogWarningAsync($"Template structure validation failed with {errors.Count} errors");
            }
            else
            {
                await _loggingService.LogInfoAsync("Template structure validation passed");
            }

            return !errors.Any();
        }

        public async Task<bool> ValidateTagMappingsAsync(CNCBrandTemplate template)
        {
            var errors = new List<ValidationError>();

            if (template.Tags == null || !template.Tags.Any())
            {
                errors.Add(new ValidationError { Field = "tags", Message = "No tags defined to validate" });
                return false;
            }

            var mappings = await _tagMappingRepository.GetMappingsByTemplateAsync(template.TemplateId);
            var mappingDict = mappings.ToDictionary(m => m.DeviceTagId);

            foreach (var tag in template.Tags)
            {
                if (!string.IsNullOrWhiteSpace(tag.DeviceTagId))
                {
                    // Check if mapping exists for device tag
                    if (!mappingDict.ContainsKey(tag.DeviceTagId))
                    {
                        errors.Add(new ValidationError 
                        { 
                            Field = $"tags[{tag.SystemTagId}].deviceTagId", 
                            Message = $"No mapping found for device tag '{tag.DeviceTagId}'" 
                        });
                    }
                    else
                    {
                        var mapping = mappingDict[tag.DeviceTagId];
                        if (!mapping.IsActive)
                        {
                            errors.Add(new ValidationError 
                            { 
                                Field = $"tags[{tag.SystemTagId}].deviceTagId", 
                                Message = $"Mapping for device tag '{tag.DeviceTagId}' is inactive" 
                            });
                        }
                    }
                }
            }

            return !errors.Any();
        }

        public async Task<bool> ValidateDataParsingRulesAsync(CNCBrandTemplate template)
        {
            var errors = new List<ValidationError>();

            if (template.DataProcessingRules == null || !template.DataProcessingRules.Any())
            {
                return true; // No rules to validate
            }

            foreach (var rule in template.DataProcessingRules)
            {
                var ruleErrors = ValidateDataProcessingRule(rule);
                errors.AddRange(ruleErrors);
            }

            return !errors.Any();
        }

        public async Task<IEnumerable<ValidationError>> ValidateTemplateForDeviceAsync(int templateId, int deviceId)
        {
            var errors = new List<ValidationError>();

            var template = await _templateRepository.GetByIdAsync(templateId);
            if (template == null)
            {
                errors.Add(new ValidationError { Field = "template", Message = "Template not found" });
                return errors;
            }

            var device = await _templateRepository.GetDeviceByIdAsync(deviceId); // Assuming this method exists
            if (device == null)
            {
                errors.Add(new ValidationError { Field = "device", Message = "Device not found" });
                return errors;
            }

            // Check if template is compatible with device brand
            if (template.BrandName != device.DeviceBrand)
            {
                errors.Add(new ValidationError 
                { 
                    Field = "template.brand", 
                    Message = $"Template brand '{template.BrandName}' does not match device brand '{device.DeviceBrand}'" 
                });
            }

            // Check for missing required tags
            var missingTags = await GetMissingRequiredTagsAsync(template);
            if (missingTags.Any())
            {
                errors.Add(new ValidationError 
                { 
                    Field = "tags", 
                    Message = $"Missing required tags: {string.Join(", ", missingTags)}" 
                });
            }

            return errors;
        }

        public async Task<bool> TestTemplateDataParsingAsync(CNCBrandTemplate template, string sampleData)
        {
            try
            {
                // Parse sample data
                var document = JsonDocument.Parse(sampleData);
                var root = document.RootElement;

                // Extract device data
                var deviceTags = new List<TagData>();
                
                if (root.TryGetProperty("tags", out var tagsElement))
                {
                    foreach (var tagElement in tagsElement.EnumerateArray())
                    {
                        var tag = new TagData
                        {
                            Id = tagElement.GetProperty("id").GetString(),
                            Desc = tagElement.GetProperty("desc").GetString(),
                            Quality = tagElement.GetProperty("quality").GetString(),
                            Time = DateTime.Parse(tagElement.GetProperty("time").GetString())
                        };

                        if (tagElement.TryGetProperty("value", out var valueElement))
                        {
                            tag.Value = ParseTagValue(valueElement);
                        }

                        deviceTags.Add(tag);
                    }
                }

                // Test tag mapping
                var mappingResult = await ValidateDeviceDataAsync(deviceTags, template.TemplateId);
                if (mappingResult.Any(r => !r.IsValid))
                {
                    await _loggingService.LogWarningAsync($"Template data parsing test failed with {mappingResult.Count(r => !r.IsValid)} validation errors");
                    return false;
                }

                // Test data processing rules
                if (template.DataProcessingRules != null)
                {
                    foreach (var rule in template.DataProcessingRules)
                    {
                        if (!await ExecuteDataProcessingRuleAsync(rule, deviceTags))
                        {
                            await _loggingService.LogWarningAsync($"Data processing rule '{rule.RuleName}' failed during test");
                            return false;
                        }
                    }
                }

                await _loggingService.LogInfoAsync("Template data parsing test passed");
                return true;
            }
            catch (Exception ex)
            {
                await _loggingService.LogErrorAsync($"Template data parsing test failed: {ex.Message}", ex);
                return false;
            }
        }

        public async Task<IEnumerable<string>> GetMissingRequiredTagsAsync(CNCBrandTemplate template)
        {
            var missingTags = new List<string>();

            if (template == null || template.Tags == null)
                return missingTags;

            var requiredTags = template.Tags
                .Where(t => t.IsRequired)
                .Select(t => t.DeviceTagId)
                .Where(id => !string.IsNullOrWhiteSpace(id))
                .ToList();

            if (!requiredTags.Any())
                return missingTags;

            var mappings = await _tagMappingRepository.GetMappingsByTemplateAsync(template.TemplateId);
            var mappedTags = mappings
                .Where(m => m.IsActive)
                .Select(m => m.DeviceTagId)
                .ToHashSet();

            foreach (var requiredTag in requiredTags)
            {
                if (!mappedTags.Contains(requiredTag))
                {
                    missingTags.Add(requiredTag);
                }
            }

            return missingTags;
        }

        public async Task<ValidationReport> ValidateTemplateComprehensivelyAsync(CNCBrandTemplate template)
        {
            var report = new ValidationReport
            {
                TemplateId = template.TemplateId,
                TemplateName = template.TemplateName,
                ValidationTime = DateTime.Now,
                Checks = new List<ValidationCheck>
                {
                    new ValidationCheck
                    {
                        Name = "Structure Validation",
                        Passed = await ValidateTemplateStructureAsync(template),
                        Errors = new List<ValidationError>()
                    },
                    new ValidationCheck
                    {
                        Name = "Tag Mapping Validation",
                        Passed = await ValidateTagMappingsAsync(template),
                        Errors = new List<ValidationError>()
                    },
                    new ValidationCheck
                    {
                        Name = "Data Parsing Rules Validation",
                        Passed = await ValidateDataProcessingRulesAsync(template),
                        Errors = new List<ValidationError>()
                    }
                }
            };

            // Aggregate all errors
            report.HasErrors = report.Checks.Any(c => !c.Passed);
            report.TotalChecks = report.Checks.Count;
            report.PassedChecks = report.Checks.Count(c => c.Passed);
            report.FailedChecks = report.Checks.Count(c => !c.Passed);

            await _loggingService.LogInformationAsync($"Comprehensive template validation: {report.PassedChecks}/{report.TotalChecks} checks passed");
            return report;
        }

        public async Task<IEnumerable<TagValidationResult>> ValidateDeviceDataAsync(IEnumerable<TagData> deviceTags, int templateId)
        {
            var results = new List<TagValidationResult>();
            var template = await _templateRepository.GetByIdAsync(templateId);

            if (template == null)
            {
                results.Add(new TagValidationResult 
                { 
                    SystemTagId = "template", 
                    IsValid = false, 
                    ErrorMessage = "Template not found" 
                });
                return results;
            }

            var templateTags = template.Tags ?? new List<TagTemplate>();
            var mappings = await _tagMappingRepository.GetMappingsByTemplateAsync(templateId);
            var mappingDict = mappings.ToDictionary(m => m.DeviceTagId);

            foreach (var templateTag in templateTags)
            {
                var result = new TagValidationResult
                {
                    SystemTagId = templateTag.SystemTagId,
                    ExpectedDataType = templateTag.DataType,
                    IsRequired = templateTag.IsRequired
                };

                if (!string.IsNullOrWhiteSpace(templateTag.DeviceTagId))
                {
                    // Find corresponding device tag
                    var deviceTag = deviceTags.FirstOrDefault(t => t.Id == templateTag.DeviceTagId);
                    
                    if (deviceTag == null)
                    {
                        if (templateTag.IsRequired)
                        {
                            result.IsValid = false;
                            result.ErrorMessage = $"Required device tag '{templateTag.DeviceTagId}' not found";
                        }
                        else
                        {
                            result.IsValid = true;
                            result.Message = "Optional tag not present";
                        }
                    }
                    else
                    {
                        // Validate device tag
                        result.DeviceTagId = templateTag.DeviceTagId;
                        result.DeviceValue = deviceTag.Value;
                        result.Quality = deviceTag.Quality;
                        
                        if (!ValidateTagValue(deviceTag, templateTag))
                        {
                            result.IsValid = false;
                            result.ErrorMessage = $"Tag value validation failed for '{templateTag.DeviceTagId}'";
                        }
                        else
                        {
                            result.IsValid = true;
                            result.MappedValue = ApplyTagMapping(deviceTag, mappingDict[templateTag.DeviceTagId]);
                        }
                    }
                }
                else
                {
                    result.IsValid = true;
                    result.Message = "No device tag mapping defined";
                }

                results.Add(result);
            }

            return results;
        }

        public async Task<bool> ValidateTemplateCompatibilityAsync(CNCBrandTemplate template1, CNCBrandTemplate template2)
        {
            if (template1 == null || template2 == null)
                return false;

            if (template1.TemplateId == template2.TemplateId)
                return true; // Same template is always compatible

            // Check if same brand
            if (template1.BrandName != template2.BrandName)
            {
                await _loggingService.LogInfoAsync($"Templates have different brands: {template1.BrandName} vs {template2.BrandName}");
                return false;
            }

            // Check tag compatibility
            var tags1 = template1.Tags ?? new List<TagTemplate>();
            var tags2 = template2.Tags ?? new List<TagTemplate>();

            // Count common tags
            var commonTags = tags1.Intersect(tags2, new TagTemplateComparer());
            var compatibilityScore = (double)commonTags.Count() / Math.Max(tags1.Count, tags2.Count);

            await _loggingService.LogInfoAsync($"Template compatibility score: {compatibilityScore:P1}");
            return compatibilityScore >= 0.8; // 80% compatibility threshold
        }

        private List<ValidationError> ValidateTagStructure(TagTemplate tag, string fieldPath)
        {
            var errors = new List<ValidationError>();

            if (string.IsNullOrWhiteSpace(tag.SystemTagId))
            {
                errors.Add(new ValidationError { Field = $"{fieldPath}.systemTagId", Message = "System tag ID is required" });
            }

            if (!string.IsNullOrWhiteSpace(tag.DeviceTagId))
            {
                if (string.IsNullOrWhiteSpace(tag.DataType))
                {
                    errors.Add(new ValidationError { Field = $"{fieldPath}.dataType", Message = "Data type is required when device tag ID is specified" });
                }
            }

            if (!string.IsNullOrWhiteSpace(tag.DataType))
            {
                var validTypes = new[] { "int", "decimal", "bool", "string", "datetime", "double" };
                if (!validTypes.Contains(tag.DataType.ToLower()))
                {
                    errors.Add(new ValidationError { Field = $"{fieldPath}.dataType", Message = $"Invalid data type: {tag.DataType}" });
                }
            }

            // Validate regex pattern if provided
            if (!string.IsNullOrWhiteSpace(tag.ValidationRegex))
            {
                try
                {
                    Regex.IsMatch("test", tag.ValidationRegex); // Test if regex is valid
                }
                catch
                {
                    errors.Add(new ValidationError { Field = $"{fieldPath}.validationRegex", Message = "Invalid regular expression pattern" });
                }
            }

            return errors;
        }

        private List<ValidationError> ValidateDataProcessingRule(DataProcessingRule rule)
        {
            var errors = new List<ValidationError>();

            if (string.IsNullOrWhiteSpace(rule.RuleName))
            {
                errors.Add(new ValidationError { Field = $"dataProcessingRules[{rule.RuleName}].ruleName", Message = "Rule name is required" });
            }

            if (string.IsNullOrWhiteSpace(rule.Condition))
            {
                errors.Add(new ValidationError { Field = $"dataProcessingRules[{rule.RuleName}].condition", Message = "Condition is required" });
            }

            if (string.IsNullOrWhiteSpace(rule.Action))
            {
                errors.Add(new ValidationError { Field = $"dataProcessingRules[{rule.RuleName}].action", Message = "Action is required" });
            }

            // Validate condition syntax (simple check)
            if (!string.IsNullOrWhiteSpace(rule.Condition) && !IsValidCondition(rule.Condition))
            {
                errors.Add(new ValidationError { Field = $"dataProcessingRules[{rule.RuleName}].condition", Message = "Invalid condition syntax" });
            }

            return errors;
        }

        private bool IsValidCondition(string condition)
        {
            // Simple condition validation (in real implementation, use expression parser)
            var validOperators = new[] { ">", "<", ">=", "<=", "==", "!=", "&&", "||", "(", ")", "true", "false" };
            var parts = condition.Split(' ');
            
            foreach (var part in parts)
            {
                if (!string.IsNullOrWhiteSpace(part) && !validOperators.Contains(part) && !double.TryParse(part, out _))
                {
                    return false;
                }
            }
            
            return true;
        }

        private object ParseTagValue(JsonElement valueElement)
        {
            if (valueElement.ValueKind == JsonValueKind.String)
            {
                return valueElement.GetString();
            }
            else if (valueElement.ValueKind == JsonValueKind.Number)
            {
                if (valueElement.TryGetInt32(out int intValue))
                    return intValue;
                else if (valueElement.TryGetDecimal(out decimal decimalValue))
                    return decimalValue;
                else
                    return valueElement.GetDouble();
            }
            else if (valueElement.ValueKind == JsonValueKind.True || valueElement.ValueKind == JsonValueKind.False)
            {
                return valueElement.GetBoolean();
            }
            else
            {
                return valueElement.ToString();
            }
        }

        private bool ValidateTagValue(TagData deviceTag, TagTemplate templateTag)
        {
            if (deviceTag.Value == null)
                return !templateTag.IsRequired;

            try
            {
                // Validate data type
                switch (templateTag.DataType.ToLower())
                {
                    case "int":
                        return int.TryParse(deviceTag.Value.ToString(), out _);
                    case "decimal":
                        return decimal.TryParse(deviceTag.Value.ToString(), out _);
                    case "bool":
                        return bool.TryParse(deviceTag.Value.ToString(), out _);
                    case "datetime":
                        return DateTime.TryParse(deviceTag.Value.ToString(), out _);
                    default:
                        return true; // String type accepts any value
                }
            }
            catch
            {
                return false;
            }
        }

        private TagData ApplyTagMapping(TagData deviceTag, TagMapping mapping)
        {
            return new TagData
            {
                Id = mapping.SystemTagId,
                Desc = mapping.Description ?? deviceTag.Desc,
                Quality = deviceTag.Quality,
                Time = deviceTag.Time,
                Value = ConvertTagValue(deviceTag.Value, mapping.DataType, mapping.ConversionFormula)
            };
        }

        private async Task<bool> ExecuteDataProcessingRuleAsync(DataProcessingRule rule, IEnumerable<TagData> deviceTags)
        {
            // Simple rule execution (in real implementation, use expression parser)
            try
            {
                // This is a simplified implementation
                // In a real system, you would parse and execute the condition and action
                
                if (rule.Condition.Contains("temperature") && deviceTags.Any(t => t.Id == "temperature"))
                {
                    var tempTag = deviceTags.First(t => t.Id == "temperature");
                    var tempValue = Convert.ToDouble(tempTag.Value);
                    
                    if (tempValue > 100 && rule.Action.Contains("alert"))
                    {
                        return true; // Rule executed successfully
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                await _loggingService.LogErrorAsync($"Failed to execute data processing rule '{rule.RuleName}': {ex.Message}");
                return false;
            }
        }
    }

    // Supporting classes and interfaces
    public class ValidationReport
    {
        public int TemplateId { get; set; }
        public string TemplateName { get; set; }
        public DateTime ValidationTime { get; set; }
        public List<ValidationCheck> Checks { get; set; }
        public bool HasErrors { get; set; }
        public int TotalChecks { get; set; }
        public int PassedChecks { get; set; }
        public int FailedChecks { get; set; }
    }

    public class ValidationCheck
    {
        public string Name { get; set; }
        public bool Passed { get; set; }
        public List<ValidationError> Errors { get; set; }
    }

    public class TagValidationResult
    {
        public string SystemTagId { get; set; }
        public string DeviceTagId { get; set; }
        public object DeviceValue { get; set; }
        public object MappedValue { get; set; }
        public string ExpectedDataType { get; set; }
        public string Quality { get; set; }
        public bool IsRequired { get; set; }
        public bool IsValid { get; set; }
        public string ErrorMessage { get; set; }
        public string Message { get; set; }
    }

    public class TagTemplateComparer : IEqualityComparer<TagTemplate>
    {
        public bool Equals(TagTemplate x, TagTemplate y)
        {
            return x?.SystemTagId == y?.SystemTagId;
        }

        public int GetHashCode(TagTemplate obj)
        {
            return obj?.SystemTagId?.GetHashCode() ?? 0;
        }
    }
}