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>
    /// Tanque de descarga hidráulico con cálculo de nivel dinámico
    /// </summary>
    public partial class osHydDischargeTank : osBase, IHydraulicTank, IosBase
    {
        #region Properties
        
        private double _area = 1.0; // m²
        private double _currentVolume = 0.5; // m³
        private double _maxVolume = 2.0; // m³
        private double _minVolume = 0.0; // m³
        private double _currentHeight = 0.0;
        private double _currentPressure = 0.0;
        
        // Propiedades visuales
        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Ancho visual del tanque en metros")]
        [property: Name("Ancho")]
        private float ancho = 0.5f;

        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Alto visual del tanque en metros")]
        [property: Name("Alto")]
        private float alto = 0.8f;

        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Color visual del tanque")]
        [property: Name("Color")]
        private Brush colorButton_oculto = Brushes.LightBlue;

        [ObservableProperty]
        [property: Category("🎨 Apariencia")]
        [property: Description("Ángulo de rotación del tanque en grados")]
        [property: Name("Ángulo")]
        private float angulo = 0f;
        
        [Category("🔧 Tanque Hidráulico")]
        [Description("Área de la base del tanque en m² (alias para compatibilidad)")]
        [Name("Área Base (m²)")]
        public double Area
        {
            get => _area;
            set => SetProperty(ref _area, Math.Max(0.1, value));
        }

        [Category("🔧 Tanque Hidráulico")]
        [Description("Presión del tanque en Pa")]
        [Name("Presión Tanque (Pa)")]
        public double TankPressure
        {
            get => _currentPressure;
            set => SetProperty(ref _currentPressure, value);
        }

        [Category("🔧 Tanque Hidráulico")]
        [Description("Nivel actual del tanque en metros")]
        [Name("Nivel (m)")]
        public double Level
        {
            get => _currentHeight;
            set 
            { 
                SetProperty(ref _currentHeight, value);
                CurrentVolume = value * Area;
            }
        }

        [Category("🔧 Tanque Hidráulico")]
        [Description("Área de la sección transversal del tanque en m²")]
        [Name("Área Sección (m²)")]
        public double CrossSectionalArea
        {
            get => _area;
            set => SetProperty(ref _area, Math.Max(0.1, value));
        }

        [Category("🔧 Tanque Hidráulico")]
        [Description("Indica si el tanque tiene presión fija")]
        [Name("Presión Fija")]
        [ReadOnly(true)]
        public bool IsFixedPressure => false; // Los tanques de descarga tienen presión calculada

        [Category("🔧 Tanque Hidráulico")]
        [Description("Volumen actual del tanque en m³")]
        [Name("Volumen Actual (m³)")]
        public double CurrentVolume
        {
            get => _currentVolume;
            set => SetProperty(ref _currentVolume, Math.Max(MinVolume, Math.Min(MaxVolume, value)));
        }

        [Category("🔧 Tanque Hidráulico")]
        [Description("Volumen máximo del tanque en m³")]
        [Name("Volumen Máximo (m³)")]
        public double MaxVolume
        {
            get => _maxVolume;
            set => SetProperty(ref _maxVolume, Math.Max(CurrentVolume, value));
        }

        [Category("🔧 Tanque Hidráulico")]
        [Description("Volumen mínimo del tanque en m³")]
        [Name("Volumen Mínimo (m³)")]
        public double MinVolume
        {
            get => _minVolume;
            set => SetProperty(ref _minVolume, Math.Max(0.0, Math.Min(CurrentVolume, value)));
        }

        [ObservableProperty]
        [property: Category("🔗 Conexiones")]
        [property: Description("Componente conectado en la entrada")]
        [property: Name("Componente Entrada")]
        [property: ItemsSource(typeof(osBaseItemsSource<IHydraulicComponent>))]
        private string id_InletComponent = "";

        [Category("📊 Estado")]
        [Description("Altura actual del líquido en metros")]
        [Name("Altura Actual (m)")]
        [ReadOnly(true)]
        public double CurrentHeight
        {
            get => _currentHeight;
            set => SetProperty(ref _currentHeight, value);
        }

        [Category("📊 Estado")]
        [Description("Presión hidrostática en el fondo del tanque en Pa")]
        [Name("Presión Fondo (Pa)")]
        [ReadOnly(true)]
        public double CurrentPressure
        {
            get => _currentPressure;
            set => SetProperty(ref _currentPressure, value);
        }

        [Category("📊 Estado")]
        [Description("Porcentaje de llenado del tanque")]
        [Name("Nivel (%)")]
        [ReadOnly(true)]
        public double FillPercentage => MaxVolume > 0 ? (CurrentVolume / MaxVolume) * 100.0 : 0.0;

        #endregion

        #region Component References
        
        [JsonIgnore]
        private IHydraulicComponent InletComponent = null;
        
        [JsonIgnore]
        private PropertyChangedEventHandler inletPropertyChangedHandler;

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

        #endregion

        #region IHydraulicComponent Implementation

        public bool HasHydraulicComponents => true;

        public List<HydraulicNodeDefinition> GetHydraulicNodes()
        {
            var nodes = new List<HydraulicNodeDefinition>();
            
            // El tanque de descarga es un nodo con presión libre (calculada)
            nodes.Add(new HydraulicNodeDefinition(Nombre, false, null, "Tanque de descarga"));
            
            return nodes;
        }

        public List<HydraulicElementDefinition> GetHydraulicElements()
        {
            // Los tanques no son elementos, son nodos
            return new List<HydraulicElementDefinition>();
        }

        public void UpdateHydraulicProperties()
        {
            // Actualizar presión basada en el nivel actual
            UpdateHeightAndPressure();
        }

        public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
        {
            // Actualizar presión del tanque
            if (pressures.TryGetValue(Nombre, out double pressure))
            {
                TankPressure = pressure;
                // Calcular nivel basado en la presión hidrostática
                Level = pressure / (1000.0 * 9.81); // Asumiendo densidad del agua
            }

            // Calcular flujo neto hacia el tanque
            double netFlow = 0.0;
            foreach (var flow in flows)
            {
                if (flow.Key.EndsWith($"->{Nombre}"))
                    netFlow += flow.Value; // Flujo entrante
                else if (flow.Key.StartsWith($"{Nombre}->"))
                    netFlow -= flow.Value; // Flujo saliente
            }

            // Aquí podrías actualizar el volumen en función del tiempo
            // Esto se haría en el update loop principal de la simulación
        }

        #endregion

        #region IHydraulicPressureReceiver Implementation

        public void SetPressure(double pressure)
        {
            TankPressure = pressure;
            // Actualizar nivel basado en presión hidrostática
            Level = pressure / (1000.0 * 9.81);
        }

        public double GetPressure()
        {
            return TankPressure;
        }

        #endregion

        #region Connection Management

        partial void OnId_InletComponentChanged(string value)
        {
            if (InletComponent != null && inletPropertyChangedHandler != null)
                ((INotifyPropertyChanged)InletComponent).PropertyChanged -= inletPropertyChangedHandler;

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

        #endregion

        #region Volume and Level Calculations

        private void UpdateHeightAndPressure()
        {
            Level = Area > 0 ? CurrentVolume / Area : 0.0;
            // Presión hidrostática en el fondo del tanque (densidad del agua = 1000 kg/m³)
            TankPressure = 1000.0 * 9.81 * Level;
        }

        /// <summary>
        /// Actualiza el volumen del tanque basado en flujos netos según la documentación
        /// </summary>
        /// <param name="flows">Diccionario de flujos de la simulación</param>
        /// <param name="deltaTime">Intervalo de tiempo en segundos</param>
        public void UpdateVolume(Dictionary<string, double> flows, double deltaTime)
        {
            double netFlow = 0.0;
            
            // Sumar flujos entrantes, restar salientes
            foreach (var flow in flows)
            {
                if (flow.Key.EndsWith($"->{Nombre}"))
                    netFlow += flow.Value; // Entrante
                else if (flow.Key.StartsWith($"{Nombre}->"))
                    netFlow -= flow.Value; // Saliente
            }
            
            // Actualizar volumen
            CurrentVolume += netFlow * deltaTime;
            CurrentVolume = Math.Max(MinVolume, Math.Min(MaxVolume, CurrentVolume));
            
            // Actualizar altura y presión
            UpdateHeightAndPressure();
        }

        /// <summary>
        /// Calcula la presión en el fondo del tanque según la documentación
        /// </summary>
        /// <param name="density">Densidad del fluido (kg/m³)</param>
        /// <returns>Presión hidrostática en Pa</returns>
        public double BottomPressure(double density = 1000.0)
        {
            return density * 9.81 * Level; // ρgh
        }

        #endregion

        #region osBase Implementation

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

        public static string NombreClase() => "Tanque de Descarga";

        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()
        {
            // Los tanques no participan en la simulación física Bepu
            // Solo en el sistema hidráulico
            UpdateHeightAndPressure();
        }

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

        public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
        {
            // Los tanques pueden tener sensores de nivel
            // Implementar aquí si es necesario
        }

        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)
        {
            UpdateHeightAndPressure();
            OnId_InletComponentChanged(Id_InletComponent);
        }

        public void Disposing()
        {
            if (InletComponent != null && inletPropertyChangedHandler != null)
                ((INotifyPropertyChanged)InletComponent).PropertyChanged -= inletPropertyChangedHandler;
        }

        #endregion

        #region Constructor

        public osHydDischargeTank()
        {
            UpdateHeightAndPressure();
        }

        #endregion
    }
}
