using System;
using System.Collections.Generic;
using System.Linq;

namespace HydraulicSimulator.Models
{
    /// <summary>
    /// Rama (elementos en serie)
    /// </summary>
    public class Branch
    {
        public string N1 { get; set; }
        public string N2 { get; set; }
        public List<Element> Elements { get; set; }
        public string Name { get; set; }
        public double Q { get; set; } = 0.0; // caudal actual (signo n1->n2)

        public Branch(string n1, string n2, List<Element> elements, string name = "")
        {
            N1 = n1;
            N2 = n2;
            Elements = new List<Element>(elements);
            Name = string.IsNullOrEmpty(name) ? $"{n1}->{n2}" : name;
        }

        public double Dp(double q, Fluid fluid)
        {
            return Elements.Sum(e => e.Dp(q, fluid));
        }

        public double DdpDq(double q, Fluid fluid)
        {
            return Elements.Sum(e => e.DdpDq(q, fluid));
        }

        /// <summary>
        /// Resuelve ΔP(q) = dpTarget por Newton 1D con amortiguación.
        /// </summary>
        public double SolveFlowGivenDp(double dpTarget, Fluid fluid)
        {
            var q = Q;
            var qMax = 1.0; // m³/s máximo razonable

            for (int i = 0; i < 50; i++) // más iteraciones
            {
                var f = Dp(q, fluid) - dpTarget;
                var df = DdpDq(q, fluid);

                // Verificar división por cero
                if (Math.Abs(df) < 1e-20)
                    df = 1e-10;

                var step = f / df;

                // Limitar el paso para estabilidad
                var maxStep = Math.Min(0.1, Math.Abs(q) * 0.5 + 0.01);
                if (Math.Abs(step) > maxStep)
                    step = Math.Sign(step) * maxStep;

                var qNew = q - step;

                // Limitar caudal dentro de rango razonable
                qNew = Math.Max(-qMax, Math.Min(qMax, qNew));

                // Amortiguación más conservadora
                var relax = i < 10 ? 0.3 : 0.1;
                q = (1 - relax) * q + relax * qNew;

                // Criterio de convergencia más estricto
                if (Math.Abs(f) < 1e-1) // Pa
                    break;

                // Debug: evitar divergencia extrema
                if (Math.Abs(q) > qMax)
                    q = Math.Sign(q) * qMax * 0.1;
            }

            Q = q;
            return q;
        }
    }
}
