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

namespace CounterDrone.Core.Tests
{
    public class DroneEntityTests
    {
        private TargetConfig CreateConfig(float speed = 120, float altitude = 300)
        {
            return new TargetConfig
            {
                TargetType = (int)TargetType.Piston,
                PowerType = (int)PowerType.Piston,
                TypicalSpeed = speed,
                TypicalAltitude = altitude,
                Wingspan = 2.5,
                Quantity = 1,
            };
        }

        private List<Waypoint> CreateRoute(float fromX, float toX, float alt, float speed)
        {
            return new List<Waypoint>
            {
                new() { PosX = fromX, PosY = alt, PosZ = 0, Altitude = alt, Speed = speed },
                new() { PosX = toX, PosY = alt, PosZ = 0, Altitude = alt, Speed = speed },
            };
        }

        [Fact]
        public void InitialPosition_IsFirstWaypoint()
        {
            var drone = new DroneEntity("d1", "g1", CreateConfig(),
                CreateRoute(100, 500, 300, 120), 0, 50, 0, 0, FormationMode.Single);
            Assert.Equal(100, drone.PosX, 1);
            Assert.Equal(300, drone.PosY, 1);
        }

        [Fact]
        public void Update_MovesTowardTarget()
        {
            var drone = new DroneEntity("d1", "g1", CreateConfig(),
                CreateRoute(0, 1000, 300, 120), 0, 50, 0, 0, FormationMode.Single);
            float prevX = drone.PosX;
            drone.Update(1f, 0, WindDirection.N);
            Assert.True(drone.PosX > prevX);
        }

        [Fact]
        public void Update_ReachesTarget_StatusChanges()
        {
            var drone = new DroneEntity("d1", "g1", CreateConfig(360),
                CreateRoute(0, 100, 300, 360), 0, 50, 0, 0, FormationMode.Single);
            drone.Update(2f, 0, WindDirection.N);
            Assert.Equal(DroneStatus.ReachedTarget, drone.Status);
        }

        [Fact]
        public void Damage_ReducesHp()
        {
            var drone = new DroneEntity("d1", "g1", CreateConfig(),
                CreateRoute(0, 100, 300, 60), 0, 50, 0, 0, FormationMode.Single);
            drone.ApplyDamage(0.4f);
            Assert.Equal(0.6f, drone.Hp, 0.01f);
        }

        [Fact]
        public void Damage_ExceedingHp_Destroys()
        {
            var drone = new DroneEntity("d1", "g1", CreateConfig(),
                CreateRoute(0, 100, 300, 60), 0, 50, 0, 0, FormationMode.Single);
            drone.ApplyDamage(1.5f);
            Assert.True(drone.Hp <= 0);
            Assert.Equal(DroneStatus.Destroyed, drone.Status);
        }

        [Fact]
        public void Wind_AffectsPosition()
        {
            var drone = new DroneEntity("d1", "g1", CreateConfig(),
                CreateRoute(0, 1000, 300, 60), 0, 50, 0, 0, FormationMode.Single);
            float xBefore = drone.PosX;
            drone.Update(1f, 20f, WindDirection.E);
            Assert.True(drone.PosX > xBefore + 15f);
        }

        [Fact]
        public void Formation_Lateral_ZOffsetsDiffer()
        {
            var route = CreateRoute(0, 100, 300, 60);
            var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 50, 0, 0, FormationMode.Formation);
            var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 1, 50, 0, 0, FormationMode.Formation);
            var d2 = new DroneEntity("d2", "g1", CreateConfig(), route, 2, 50, 0, 0, FormationMode.Formation);
            // 起始Z不同
            Assert.NotEqual(d0.PosZ, d1.PosZ);
            Assert.NotEqual(d1.PosZ, d2.PosZ);
            // 航点Z不同（偏移持久化到航点）
            Assert.NotEqual(d0.Route[0].PosZ, d1.Route[0].PosZ);
            Assert.NotEqual(d0.Route[1].PosZ, d1.Route[1].PosZ);
        }

        [Fact]
        public void Formation_Longitudinal_XOffsetsDiffer()
        {
            var route = CreateRoute(0, 100, 300, 60);
            var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 0, 0, 100, FormationMode.Formation);
            var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 0, 0, 1, 100, FormationMode.Formation);
            Assert.NotEqual(d0.PosX, d1.PosX);
        }

        [Fact]
        public void Formation_Mixed_2x2()
        {
            var route = CreateRoute(0, 100, 300, 60);
            var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 50, 0, 100, FormationMode.Formation);
            var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 1, 50, 0, 100, FormationMode.Formation);
            var d2 = new DroneEntity("d2", "g1", CreateConfig(), route, 0, 50, 1, 100, FormationMode.Formation);
            var d3 = new DroneEntity("d3", "g1", CreateConfig(), route, 1, 50, 1, 100, FormationMode.Formation);
            Assert.NotEqual(d0.PosZ, d1.PosZ); // lateral
            Assert.NotEqual(d0.PosX, d2.PosX); // longitudinal
        }

        [Fact]
        public void Swarm_ProducesRandomOffsets()
        {
            var route = CreateRoute(0, 100, 300, 60);
            var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 30, 0, 0, FormationMode.Swarm);
            var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 1, 30, 0, 0, FormationMode.Swarm);
            Assert.True(d0.PosX != d1.PosX || d0.PosZ != d1.PosZ);
        }
    }
}
