using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using Xunit;

namespace CounterDrone.Core.Tests
{
    public class DispersionModelTests
    {
        private static AmmunitionSpec Ammo() => DefaultAmmunition.GetByType(AerosolType.InertGas);

        [Fact]
        public void Phase1_InitialRadius_FromBurstCharge()
        {
            var m = new GaussianPuffDispersion();
            m.Initialize(Ammo(), new CombatScene { WindSpeed = 0 }, new Algorithms.Vector3(0, 0, 0), 0f);
            // R₀ = 3.3 × 0.3^0.32 ≈ 2.2m
            Assert.True(m.Radius > 1.5f && m.Radius < 5f, $"R₀={m.Radius:F2}");
        }

        [Fact]
        public void Phase2_Radius_GrowsWithSqrtT()
        {
            var m = new GaussianPuffDispersion();
            m.Initialize(Ammo(), new CombatScene { WindSpeed = 0 }, new Algorithms.Vector3(0, 0, 0), 0f);
            var r1 = m.Radius;
            m.Tick(9f, 0f, WindDirection.N);
            var r2 = m.Radius;
            m.Tick(7f, 0f, WindDirection.N); // total 16s
            var r3 = m.Radius;
            // 0→9s: Δ = k×3 = 15; 9→16s: Δ = k×(4-3) = 5
            Assert.True(r3 > r2 && r2 > r1, "半径应单调递增");
        }

        [Fact]
        public void Phase2_Density_DropsWithVolume()
        {
            var m = new GaussianPuffDispersion();
            m.Initialize(Ammo(), new CombatScene { WindSpeed = 0 }, new Algorithms.Vector3(0, 0, 0), 0f);
            var d1 = m.CoreDensity;
            m.Tick(10f, 0f, WindDirection.N);
            Assert.True(m.CoreDensity < d1, "膨胀后密度应下降");
        }

        [Fact]
        public void Phase3_SwitchesAfter30s()
        {
            var m = new GaussianPuffDispersion();
            m.Initialize(Ammo(), new CombatScene { WindSpeed = 0 }, new Algorithms.Vector3(0, 0, 0), 0f);
            m.Tick(35f, 0f, WindDirection.N);
            Assert.False(m.IsDissipated, "35s不应消散");
        }

        [Fact]
        public void Dissipates_AfterMaxDuration()
        {
            var m = new GaussianPuffDispersion();
            var a = Ammo();
            a.MaxDuration = 2;
            m.Initialize(a, new CombatScene { WindSpeed = 0 }, new Algorithms.Vector3(0, 0, 0), 0f);
            m.Tick(3f, 0f, WindDirection.N);
            Assert.True(m.IsDissipated);
        }

        [Fact]
        public void DefaultAmmo_ParametersInReasonableRange()
        {
            var inert = DefaultAmmunition.GetByType(AerosolType.InertGas);
            Assert.True(inert.BurstChargeKg is > 0.01 and < 5, "爆发药应 0.01~5kg");
            Assert.True(inert.TurbulentExpansionK is > 1 and < 20, "湍流系数应 1~20");
            Assert.True(inert.EffectiveConcentration is >= 0.0001 and < 0.1, "有效浓度阈值应 ≥ 0.0001");
            Assert.True(inert.SourceStrength is > 1 and < 100, "源强应 1~100kg");
        }
    }
}
