using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MainShell.DeviceMaintance.Model
{
    public static class CylinderDefinitionLoader
    {
        private const string CylinderConfigFileName = "CylinderDefinitions.csv";

        public static IReadOnlyList<CylinderDefinition> LoadDefinitions()
        {
            var definitions = new List<CylinderDefinition>();
            var path = ResolveConfigPath();
            if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
            {
                return definitions;
            }

            var lines = File.ReadAllLines(path, Encoding.UTF8);
            foreach (var raw in lines)
            {
                if (string.IsNullOrWhiteSpace(raw))
                {
                    continue;
                }

                var line = raw.Trim();
                if (line.StartsWith("#") || line.StartsWith("Name", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                var cols = line.Split(',');
                if (cols.Length < 7)
                {
                    continue;
                }

                var extendOutputPoints = ParsePointReferences(cols[3]);
                if (extendOutputPoints.Count == 0)
                {
                    continue;
                }

                var retractOutputPoints = ParsePointReferences(cols[4]);

                var hasConditionColumns = cols.Length >= 10;
                var extendConditionIndex = hasConditionColumns ? 7 : -1;
                var retractConditionIndex = hasConditionColumns ? 8 : -1;
                var descriptionIndex = hasConditionColumns ? 9 : 7;

                definitions.Add(new CylinderDefinition
                {
                    Name = cols[0].Trim(),
                    Module = cols[1].Trim(),
                    ControlType = ParseControlType(cols[2]),
                    ExtendOutputPoints = extendOutputPoints,
                    RetractOutputPoints = retractOutputPoints,
                    ExtendedFeedbackPoints = ParsePointReferences(cols[5]),
                    RetractedFeedbackPoints = ParsePointReferences(cols[6]),
                    ExtendConditions = extendConditionIndex >= 0 ? ParseConditions(cols[extendConditionIndex]) : new List<CylinderActionConditionDefinition>(),
                    RetractConditions = retractConditionIndex >= 0 ? ParseConditions(cols[retractConditionIndex]) : new List<CylinderActionConditionDefinition>(),
                    Description = cols.Length > descriptionIndex ? string.Join(",", cols.Skip(descriptionIndex)).Trim() : string.Empty
                });
            }

            return definitions;
        }

        private static CylinderControlType ParseControlType(string raw)
        {
            if (string.Equals(raw, "DualOutput", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(raw, "Dual", StringComparison.OrdinalIgnoreCase))
            {
                return CylinderControlType.DualOutput;
            }

            if (string.Equals(raw, "MultiOutput", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(raw, "Multi", StringComparison.OrdinalIgnoreCase))
            {
                return CylinderControlType.MultiOutput;
            }

            return CylinderControlType.SingleOutput;
        }

        private static List<string> ParsePointReferences(string raw)
        {
            return (raw ?? string.Empty)
                .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(x => x.Trim())
                .Where(x => !string.IsNullOrWhiteSpace(x))
                .ToList();
        }

        private static List<CylinderActionConditionDefinition> ParseConditions(string raw)
        {
            return (raw ?? string.Empty)
                .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(ParseCondition)
                .Where(x => x != null)
                .ToList();
        }

        private static CylinderActionConditionDefinition ParseCondition(string raw)
        {
            if (string.IsNullOrWhiteSpace(raw))
            {
                return null;
            }

            var parts = raw.Split(new[] { ':' }, 3);
            if (parts.Length < 2)
            {
                return null;
            }

            var pointReference = parts[1].Trim();
            if (string.IsNullOrWhiteSpace(pointReference))
            {
                return null;
            }

            CylinderConditionType conditionType;
            switch ((parts[0] ?? string.Empty).Trim().ToUpperInvariant())
            {
                case "POINTON":
                case "ON":
                    conditionType = CylinderConditionType.PointOn;
                    break;
                case "POINTOFF":
                case "OFF":
                    conditionType = CylinderConditionType.PointOff;
                    break;
                default:
                    return null;
            }

            return new CylinderActionConditionDefinition
            {
                ConditionType = conditionType,
                PointReference = pointReference,
                Message = parts.Length > 2 ? parts[2].Trim() : string.Empty
            };
        }

        private static string ResolveConfigPath()
        {
            var baseDir = AppDomain.CurrentDomain.BaseDirectory;
            var localPath = Path.Combine(baseDir, "Configuration", CylinderConfigFileName);
            var sourcePath = ResolveRootConfigPath(baseDir);

            if (TrySyncConfigToLocal(sourcePath, localPath))
            {
                return localPath;
            }

            return File.Exists(localPath) ? localPath : sourcePath;
        }

        private static string ResolveRootConfigPath(string baseDir)
        {
            var current = new DirectoryInfo(baseDir);
            while (current != null)
            {
                var rootConfigDir = Path.Combine(current.FullName, "Configuration");
                if (Directory.Exists(rootConfigDir) && Directory.Exists(Path.Combine(current.FullName, "MainShell")))
                {
                    var path = Path.Combine(rootConfigDir, CylinderConfigFileName);
                    if (File.Exists(path))
                    {
                        return path;
                    }
                }

                current = current.Parent;
            }

            return null;
        }

        private static bool TrySyncConfigToLocal(string sourcePath, string localPath)
        {
            if (string.IsNullOrWhiteSpace(sourcePath) || !File.Exists(sourcePath))
            {
                return false;
            }

            if (!string.Equals(Path.GetFullPath(sourcePath), Path.GetFullPath(localPath), StringComparison.OrdinalIgnoreCase))
            {
                var localDir = Path.GetDirectoryName(localPath);
                if (!string.IsNullOrWhiteSpace(localDir))
                {
                    Directory.CreateDirectory(localDir);
                }

                if (!File.Exists(localPath) || File.GetLastWriteTimeUtc(localPath) < File.GetLastWriteTimeUtc(sourcePath))
                {
                    File.Copy(sourcePath, localPath, true);
                }
            }

            return true;
        }
    }
}
