using System.Collections.Generic;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using Xunit;

namespace CounterDrone.Core.Tests
{
    public class DetectionCalculatorTests
    {
        private static List<Waypoint> Line(float x0, float z0, float x1, float z1)
            => new()
            {
                new Waypoint { PosX = x0, PosY = 500, PosZ = z0 },
                new Waypoint { PosX = x1, PosY = 500, PosZ = z1 },
            };

        // ═══════════════════════════════════════
        // EffectiveRange — 天气衰减
        // ═══════════════════════════════════════

        [Fact]
        public void EffectiveRange_EO_VisibilityBelowBase_ScalesDown()
        {
            // 光电基准 10000m，能见度 5000m → 有效 = 10000 × (5000/10000) = 5000
            float r = DetectionCalculator.EffectiveRange(0, 10000, 0, 5000);
            Assert.Equal(5000f, r, 0);
        }

        [Fact]
        public void EffectiveRange_EO_VisibilityAboveBase_NoEffect()
        {
            // 光电基准 10000m，能见度 15000m → 有效 = 10000（min(1, 1.5)=1）
            float r = DetectionCalculator.EffectiveRange(0, 10000, 0, 15000);
            Assert.Equal(10000f, r, 0);
        }

        [Fact]
        public void EffectiveRange_Radar_UnaffectedByVisibility()
        {
            // 雷达 15000m，能见度只有 1000m，雷达不受影响
            float r = DetectionCalculator.EffectiveRange(15000, 0, 0, 1000);
            Assert.Equal(15000f, r, 0);
        }

        [Fact]
        public void EffectiveRange_IR_UnaffectedByVisibility()
        {
            float r = DetectionCalculator.EffectiveRange(0, 0, 8000, 500);
            Assert.Equal(8000f, r, 0);
        }

        [Fact]
        public void EffectiveRange_TakesMaxOfMethods()
        {
            // 雷达 12000 + 光电 6000(能见度 3000 → 3000) + 红外 8000 → max=12000
            float r = DetectionCalculator.EffectiveRange(12000, 6000, 8000, 3000);
            Assert.Equal(12000f, r, 0);
        }

        [Fact]
        public void EffectiveRange_EO_StrongestWhenGoodWeather()
        {
            // 雷达 8000 + 光电 15000(能见度 15000) + 红外 10000 → max=15000（光电胜出）
            float r = DetectionCalculator.EffectiveRange(8000, 15000, 10000, 15000);
            Assert.Equal(15000f, r, 0);
        }

        // ═══════════════════════════════════════
        // EarliestDetection — 最早探测点
        // ═══════════════════════════════════════

        [Fact]
        public void EarliestDetection_DroneEntersCircle_ReturnsEntryArc()
        {
            // 航路 X:0→10000 Z:0，探测源在 X=7000，半径 5000
            // 探测圆边界在 X=2000 和 X=12000，无人机从 X=0 飞向 10000，在 X=2000 进入
            // 弧长 = 2000
            var route = Line(0, 0, 10000, 0);
            var src = new DetectionSource
            {
                Position = new Vector3(7000, 0, 0),
                RadarRange = 5000, Accuracy = 100,
            };
            var (arc, acc) = DetectionCalculator.EarliestDetection(route, new() { src }, 10000);
            Assert.InRange(arc, 1950f, 2050f); // X=2000 进入圆
            Assert.Equal(100f, acc, 0);
        }

        [Fact]
        public void EarliestDetection_DroneStartsInside_ReturnsZero()
        {
            // 航路起点已在探测圆内
            var route = Line(6000, 0, 10000, 0);
            var src = new DetectionSource
            {
                Position = new Vector3(5000, 0, 0),
                RadarRange = 5000, Accuracy = 50,
            };
            var (arc, _) = DetectionCalculator.EarliestDetection(route, new() { src }, 10000);
            Assert.Equal(0f, arc, 0);
        }

        [Fact]
        public void EarliestDetection_NeverEnters_ReturnsMaxValue()
        {
            // 航路离探测源很远
            var route = Line(0, 50000, 10000, 50000);
            var src = new DetectionSource
            {
                Position = new Vector3(5000, 0, 0),
                RadarRange = 1000,
            };
            var (arc, _) = DetectionCalculator.EarliestDetection(route, new() { src }, 10000);
            Assert.Equal(float.MaxValue, arc);
        }

        [Fact]
        public void EarliestDetection_MultipleSources_TakesEarliest()
        {
            // 两个探测源：源A 探测圆边界 X=8000，源B 边界 X=3000
            // 无人机从 X=0 飞，最早在 X=3000 被源B 发现
            var route = Line(0, 0, 20000, 0);
            var sources = new List<DetectionSource>
            {
                new() { Position = new Vector3(12000, 0, 0), RadarRange = 4000, Accuracy = 200 },  // 边界 X=8000
                new() { Position = new Vector3(7000, 0, 0), RadarRange = 4000, Accuracy = 80 },    // 边界 X=3000
            };
            var (arc, acc) = DetectionCalculator.EarliestDetection(route, sources, 10000);
            Assert.InRange(arc, 2950f, 3050f);
            Assert.Equal(80f, acc, 0); // 用源B的精度
        }

        [Fact]
        public void EarliestDetection_NoSources_ReturnsZeroArc()
        {
            // 无探测设备，弧长=0（上帝视角，从起点算）
            var route = Line(0, 0, 10000, 0);
            var (arc, acc) = DetectionCalculator.EarliestDetection(route, new List<DetectionSource>(), 10000);
            Assert.Equal(0f, arc, 0);
            Assert.Equal(float.MaxValue, acc); // 无精度信息
        }

        [Fact]
        public void EarliestDetection_EO_WeatherShortensDetection()
        {
            // 光电 10000m，能见度 4000 → 有效 4000m
            // 探测源在 X=9000，有效圆边界 X=5000 和 X=13000
            // 无人机从 X=0 飞，在 X=5000 进入
            var route = Line(0, 0, 15000, 0);
            var src = new DetectionSource
            {
                Position = new Vector3(9000, 0, 0),
                EORange = 10000, Accuracy = 100,
            };
            var (arc, _) = DetectionCalculator.EarliestDetection(route, new() { src }, 4000);
            Assert.InRange(arc, 4950f, 5050f);
        }

        // ═══════════════════════════════════════
        // SpreadRadius — 精度换算散布
        // ═══════════════════════════════════════

        [Fact]
        public void SpreadRadius_EqualsAccuracy()
        {
            Assert.Equal(100f, DetectionCalculator.SpreadRadius(100f), 0);
            Assert.Equal(0f, DetectionCalculator.SpreadRadius(0f), 0);
        }

        // ═══════════════════════════════════════
        // IsInCoverage — 3D 球冠判定
        // ═══════════════════════════════════════

        [Fact]
        public void IsInCoverage_Inside3DCap_ReturnsTrue()
        {
            // 设备在 (0,0,0)，目标在 (3000, 500, 4000) → 水平距离5000，高度差500
            // 俯仰角 ≈ atan2(500, 5000) ≈ 5.7°
            // 无限制球冠 → 通过
            var pos = new Vector3(3000, 500, 4000);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 6000f,
                float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue);
            Assert.True(result);
        }

        [Fact]
        public void IsInCoverage_HorizontalTooFar_ReturnsFalse()
        {
            // 水平距离 8000 > 有效范围 5000
            var pos = new Vector3(6000, 500, 6000);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 5000f,
                float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue);
            Assert.False(result);
        }

        [Fact]
        public void IsInCoverage_BelowMinDetectAlt_ReturnsFalse()
        {
            // 高度门限 200-1000，目标在 100m → 被拒绝
            var pos = new Vector3(3000, 100, 4000);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 6000f,
                float.MaxValue, float.MaxValue, 200f, 1000f);
            Assert.False(result);
        }

        [Fact]
        public void IsInCoverage_AboveMaxDetectAlt_ReturnsFalse()
        {
            // 高度门限 200-1000，目标在 1200m → 被拒绝
            var pos = new Vector3(3000, 1200, 4000);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 6000f,
                float.MaxValue, float.MaxValue, 200f, 1000f);
            Assert.False(result);
        }

        [Fact]
        public void IsInCoverage_ElevationTooLow_ReturnsFalse()
        {
            // 俯仰角限 -2° 到 30°，目标几乎在正下方(-89°) → 被拒绝
            var pos = new Vector3(1, -5000, 1);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 6000f,
                -2f, 30f, float.MaxValue, float.MaxValue);
            Assert.False(result);
        }

        [Fact]
        public void IsInCoverage_ElevationTooHigh_ReturnsFalse()
        {
            // 俯仰角限 -2° 到 30°，目标仰角 ~45° → 被拒绝
            var pos = new Vector3(3000, 3000, 4000);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 6000f,
                -2f, 30f, float.MaxValue, float.MaxValue);
            Assert.False(result);
        }

        [Fact]
        public void IsInCoverage_DirectlyAbove_WithElevationLimit_ReturnsTrue()
        {
            // 正顶（horizDist≈0），即使有俯仰角限制也应通过（物理上正上方肯定可见）
            var pos = new Vector3(0, 1000, 0);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 2000f,
                -2f, 30f, float.MaxValue, float.MaxValue);
            Assert.True(result);
        }

        [Fact]
        public void IsInCoverage_UnlimitedCaps_EquivalentTo2DCircle()
        {
            // 所有限制为 MaxValue（无限制），等价于 2D 圆
            var pos = new Vector3(3000, float.MaxValue / 2, 4000);
            var det = new Vector3(0, 0, 0);
            var result = DetectionCalculator.IsInCoverage(pos, det, 6000f,
                float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue);
            Assert.True(result);
        }
    }
}
