using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Media;
using CtrEditor.HydraulicSimulator;
using CtrEditor.ObjetosSim;
using CtrEditor.FuncionesBase;
using HydraulicSimulator.Models;
using Newtonsoft.Json;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;

namespace CtrEditor.ObjetosSim.HydraulicComponents
{
    /// <summary>
    /// Tubería hidráulica que conecta componentes del sistema hidráulico
    /// </summary>
    public partial class osHydPipe : osBase, IHydraulicPipe, IosBase
    {
        #region Properties
        
        private double _length = 1.0; // metros
        private double _diameter = 0.05; // metros (50mm)
        private double _roughness = 4.5e-5; // metros - rugosidad del acero comercial
        private double _currentFlow = 0.0;
        private double _pressureDrop = 0.0;
        
        // Propiedades visuales
        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Ancho visual de la tubería en metros")]
        [property: Name("Ancho")]
        private float ancho = 1.0f;

        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Alto visual de la tubería en metros")]
        [property: Name("Alto")]
        private float alto = 0.05f;

        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Color visual de la tubería")]
        [property: Name("Color")]
        private Brush colorButton_oculto = Brushes.Gray;

        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Ángulo de rotación de la tubería en grados")]
        [property: Name("Ángulo")]
        private float angulo = 0f;
        
        [Category("🔧 Tubería Hidráulica")]
        [Description("Longitud de la tubería en metros")]
        [Name("Longitud (m)")]
        public double Length
        {
            get => _length;
            set => SetProperty(ref _length, Math.Max(0.1, value));
        }

        [Category("🔧 Tubería Hidráulica")]
        [Description("Diámetro interno de la tubería en metros")]
        [Name("Diámetro (m)")]
        public double Diameter
        {
            get => _diameter;
            set => SetProperty(ref _diameter, Math.Max(0.001, value));
        }

        [Category("🔧 Tubería Hidráulica")]
        [Description("Rugosidad del material en metros")]
        [Name("Rugosidad (m)")]
        public double Roughness
        {
            get => _roughness;
            set => SetProperty(ref _roughness, Math.Max(1e-6, value));
        }

        [ObservableProperty]
        [property: Category("🔗 Conexiones")]
        [property: Description("Primer componente hidráulico conectado")]
        [property: Name("Componente A")]
        [property: ItemsSource(typeof(osBaseItemsSource<IHydraulicComponent>))]
        private string id_ComponenteA = "";

        [ObservableProperty]
        [property: Category("🔗 Conexiones")]
        [property: Description("Segundo componente hidráulico conectado")]
        [property: Name("Componente B")]
        [property: ItemsSource(typeof(osBaseItemsSource<IHydraulicComponent>))]
        private string id_ComponenteB = "";

        [Category("📊 Estado")]
        [Description("Flujo actual a través de la tubería en m³/s")]
        [Name("Flujo Actual (m³/s)")]
        [ReadOnly(true)]
        public double CurrentFlow
        {
            get => _currentFlow;
            set => SetProperty(ref _currentFlow, value);
        }

        [Category("📊 Estado")]
        [Description("Pérdida de presión en la tubería en Pa")]
        [Name("Pérdida Presión (Pa)")]
        [ReadOnly(true)]
        public double PressureDrop
        {
            get => _pressureDrop;
            set => SetProperty(ref _pressureDrop, value);
        }

        private string pipeId = "";
        public string PipeId 
        { 
            get => pipeId; 
            set => SetProperty(ref pipeId, value); 
        }

        #endregion

        #region Component References
        
        [JsonIgnore]
        private IHydraulicComponent ComponenteA = null;
        
        [JsonIgnore]
        private IHydraulicComponent ComponenteB = null;
        
        [JsonIgnore]
        private PropertyChangedEventHandler componenteAPropertyChangedHandler;
        
        [JsonIgnore]
        private PropertyChangedEventHandler componenteBPropertyChangedHandler;

        [JsonIgnore]
        public Action<float, float> ActualizarTamaño { get; set; }

        #endregion

        #region IHydraulicPipe Implementation
        
        // Ya implementadas las propiedades Length, Diameter, Roughness arriba

        #endregion

        #region IHydraulicComponent Implementation

        public bool HasHydraulicComponents => true;

        public List<HydraulicNodeDefinition> GetHydraulicNodes()
        {
            var nodes = new List<HydraulicNodeDefinition>();
            
            // Las tuberías conectan componentes existentes, no crean nodos propios
            // Los nodos son creados por los componentes conectados (tanques, bombas, etc.)
            // La tubería solo actúa como elemento que conecta nodos existentes
            
            return nodes;
        }

        public List<HydraulicElementDefinition> GetHydraulicElements()
        {
            var elements = new List<HydraulicElementDefinition>();
            
            // Solo crear elemento si ambos componentes están conectados
            if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
            {
                // Crear el elemento Pipe según la documentación
                var pipeElement = new Pipe(Length, Diameter, Roughness);
                
                elements.Add(new HydraulicElementDefinition(
                    name: $"{Nombre}_Pipe",
                    fromNode: Id_ComponenteA,  // Usar el nombre del componente A como nodo origen
                    toNode: Id_ComponenteB,    // Usar el nombre del componente B como nodo destino
                    element: pipeElement,
                    description: $"Tubería {Nombre} - L:{Length:F2}m, D:{Diameter*1000:F0}mm ({Id_ComponenteA} → {Id_ComponenteB})"
                ));
            }
            
            return elements;
        }

        public void UpdateHydraulicProperties()
        {
            // Actualizar propiedades antes de la simulación si es necesario
            // Por ejemplo, cambios dinámicos en diámetro o rugosidad
        }

        public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
        {
            // Solo procesar si ambos componentes están conectados
            if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
            {
                string branchKey = $"{Id_ComponenteA}->{Id_ComponenteB}";
                
                if (flows.TryGetValue(branchKey, out double flow))
                {
                    CurrentFlow = flow;
                }
                
                // Calcular pérdida de presión entre nodos
                if (pressures.TryGetValue(Id_ComponenteA, out double pressureA) &&
                    pressures.TryGetValue(Id_ComponenteB, out double pressureB))
                {
                    PressureDrop = pressureA - pressureB;
                }
            }
        }

        #endregion

        #region IHydraulicFlowReceiver Implementation

        public void SetFlow(double flow)
        {
            CurrentFlow = flow;
        }

        public double GetFlow()
        {
            return CurrentFlow;
        }

        #endregion

        #region IHydraulicPressureReceiver Implementation

        public void SetPressure(double pressure)
        {
            // Para tuberías, la presión se maneja a través de los nodos
        }

        public double GetPressure()
        {
            return PressureDrop;
        }

        #endregion

        #region Connection Management

        partial void OnId_ComponenteAChanged(string value)
        {
            if (ComponenteA != null && componenteAPropertyChangedHandler != null)
                ((INotifyPropertyChanged)ComponenteA).PropertyChanged -= componenteAPropertyChangedHandler;

            if (_mainViewModel != null && !string.IsNullOrEmpty(value))
            {
                ComponenteA = (IHydraulicComponent)_mainViewModel.ObjetosSimulables
                    .FirstOrDefault(s => s is IHydraulicComponent comp && 
                                   ((osBase)comp).Nombre == value);
                
                if (ComponenteA != null)
                {
                    componenteAPropertyChangedHandler = (sender, e) =>
                    {
                        if (e.PropertyName == nameof(osBase.Nombre))
                        {
                            Id_ComponenteA = ((osBase)sender).Nombre;
                        }
                    };
                    ((INotifyPropertyChanged)ComponenteA).PropertyChanged += componenteAPropertyChangedHandler;
                }
            }
        }

        partial void OnId_ComponenteBChanged(string value)
        {
            if (ComponenteB != null && componenteBPropertyChangedHandler != null)
                ((INotifyPropertyChanged)ComponenteB).PropertyChanged -= componenteBPropertyChangedHandler;

            if (_mainViewModel != null && !string.IsNullOrEmpty(value))
            {
                ComponenteB = (IHydraulicComponent)_mainViewModel.ObjetosSimulables
                    .FirstOrDefault(s => s is IHydraulicComponent comp && 
                                   ((osBase)comp).Nombre == value);
                
                if (ComponenteB != null)
                {
                    componenteBPropertyChangedHandler = (sender, e) =>
                    {
                        if (e.PropertyName == nameof(osBase.Nombre))
                        {
                            Id_ComponenteB = ((osBase)sender).Nombre;
                        }
                    };
                    ((INotifyPropertyChanged)ComponenteB).PropertyChanged += componenteBPropertyChangedHandler;
                }
            }
        }

        #endregion

        #region osBase Implementation

        public static string NombreCategoria() => "Componentes Hidráulicos";

        public static string NombreClase() => "Tubería Hidráulica";

        private string nombre = NombreClase();

        [Category("Identificación")]
        [Description("Nombre identificativo del objeto")]
        [Name("Nombre")]
        public override string Nombre
        {
            get => nombre;
            set => SetProperty(ref nombre, value);
        }

        public override void AltoChanged(float value)
        {
            ActualizarGeometrias();
        }

        public override void AnchoChanged(float value)
        {
            ActualizarGeometrias();
        }

        public void Start()
        {
            // Las tuberías no participan en la simulación física Bepu
            // Solo en el sistema hidráulico
        }

        public override void UpdateGeometryStart()
        {
            ActualizarGeometrias();
        }

        public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
        {
            // Las tuberías no tienen control PLC directo
        }

        private void ActualizarGeometrias()
        {
            try
            {
                // Actualizar geometría visual
                if (this.ActualizarTamaño != null)
                    this.ActualizarTamaño(Ancho, Alto);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error actualizando geometría: {ex.Message}");
            }
        }

        public void Inicializar(int valorInicial)
        {
            PipeId = $"Pipe_{Nombre}_{valorInicial}";
            OnId_ComponenteAChanged(Id_ComponenteA);
            OnId_ComponenteBChanged(Id_ComponenteB);
        }

        public void Disposing()
        {
            if (ComponenteA != null && componenteAPropertyChangedHandler != null)
                ((INotifyPropertyChanged)ComponenteA).PropertyChanged -= componenteAPropertyChangedHandler;
            
            if (ComponenteB != null && componenteBPropertyChangedHandler != null)
                ((INotifyPropertyChanged)ComponenteB).PropertyChanged -= componenteBPropertyChangedHandler;
        }

        #endregion

        #region Constructor

        public osHydPipe()
        {
            PipeId = Guid.NewGuid().ToString();
        }

        #endregion
    }
}
