using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CounterDrone.Core;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using CounterDrone.Core.Repository;
using CounterDrone.Core.Services;
using CounterDrone.Core.Simulation;
using SQLite;
using Xunit;

namespace CounterDrone.Core.Tests
{
    public class FullPipelineTests : IDisposable
    {
        private readonly string _testDir;
        private readonly IPathProvider _paths;
        private readonly IScenarioService _scenario;
        private readonly FrameDataStore _frameStore;
        private readonly SQLiteConnection _mainDb;
        private List<AmmunitionSpec> _ammoCatalog;
        private string _taskId = string.Empty;

        public FullPipelineTests()
        {
            _testDir = Path.Combine(Path.GetTempPath(), $"cd_full_{Guid.NewGuid():N}");
            _paths = new TestPathProvider(_testDir);
            var dbm = new DatabaseManager(_paths);
            _mainDb = dbm.OpenMainDb();
            foreach (var a in DefaultAmmunition.GetAll()) _mainDb.Insert(a);
            _ammoCatalog = new List<AmmunitionSpec>(DefaultAmmunition.GetAll());

            _scenario = new ScenarioService(
                new SimTaskRepository(_mainDb), new CombatSceneRepository(_mainDb),
                new ControlZoneRepository(_mainDb), new TargetConfigRepository(_mainDb),
                new EquipmentDeploymentRepository(_mainDb), new CloudDispersalRepository(_mainDb),
                new RoutePlanRepository(_mainDb), new WaypointRepository(_mainDb));

            _frameStore = new FrameDataStore(_paths);
        }

        public void Dispose()
        {
            _mainDb?.Close();
            if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true);
        }

        private SimulationEngine RunSimulation(int maxTicks, float tickDt = 1f / 20f, float timeScale = 8f)
        {
            var engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefaultDefensePlanner(_ammoCatalog));

            engine.Initialize(_taskId);
            engine.TimeScale = timeScale;  // 8倍速加速
            for (int i = 0; i < maxTicks; i++)
            {
                var r = engine.Tick(tickDt);
                if (r.State == SimulationState.Completed) break;
            }
            engine.Stop();
            return engine;
        }

        private static readonly string ReportOutputDir = Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..", "..", "reports");

        private static EquipmentDeployment MakeEquipment(FireUnitTemplate template, AerosolType ammoType, int quantity, double posX, double posY, double posZ)
        {
            return new EquipmentDeployment
            {
                EquipmentRole = (int)EquipmentRole.LaunchPlatform,
                PlatformType = template.PlatformType,
                Quantity = quantity,
                PositionX = posX, PositionY = posY, PositionZ = posZ,
                AerosolType = (int)ammoType,
                MunitionCount = template.GunCount * template.ChannelsPerGun,
                GunCount = template.GunCount,
                ChannelsPerGun = template.ChannelsPerGun,
                ChannelInterval = template.ChannelInterval,
                Cooldown = template.Cooldown,
                MuzzleVelocity = template.MuzzleVelocity > 0 ? template.MuzzleVelocity : null,
                CruiseSpeed = template.CruiseSpeed > 0 ? template.CruiseSpeed : null,
                ReleaseAltitude = template.ReleaseAltitude > 0 ? template.ReleaseAltitude : null,
            };
        }

        private void VerifyAndExportReport(SimulationEngine eng, DroneEntity drone)
        {
            var detail = _scenario.GetTaskDetail(_taskId)!;
            var report = new ReportGenerator().Generate(detail, eng.Events.ToList(), drone.Status, eng.SimulationTime);
            Assert.Contains("对抗结果", report);
            Assert.Contains(detail.Task.Name, report);

            var svc = new ReportService(_mainDb, _paths);
            var r = svc.Generate(_taskId, detail, eng.Events.ToList(), drone.Status.ToString(), eng.SimulationTime);
            svc.ExportToFile(r.Id, ReportOutputDir);  // 持久化目录
        }

        // ═══════════════════════════════════════════════
        // 场景 1：无防御 — 无人机到达目标
        // ═══════════════════════════════════════════════

        [Fact]
        public void Scenario_NoDefense_DroneReachesTarget()
        {
            var task = _scenario.CreateTask("无防御测试", "");
            _taskId = task.Id;

            _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
            _scenario.SaveTarget(_taskId, new TargetConfig
            {
                GroupId = "default",
                TargetType = (int)TargetType.FixedWing,
                PowerType = (int)PowerType.Jet,
                Quantity = 1,
                TypicalSpeed = 600,
                TypicalAltitude = 400,
            });
            _scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
                new List<Waypoint>
                {
                    new Waypoint { PosX = 0, PosY = 400, PosZ = 0, Speed = 600 },
                    new Waypoint { PosX = 3000, PosY = 400, PosZ = 0, Speed = 600 },
                });
            _scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
            _scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());

            var eng = RunSimulation(400);
            Assert.Equal(DroneStatus.ReachedTarget, eng.Drones[0].Status);
        }

        // ═══════════════════════════════════════════════
        // 场景 2：管控区域侵入
        // ═══════════════════════════════════════════════

        [Fact]
        public void Scenario_ZoneIntrusion_DroneMarked()
        {
            var task = _scenario.CreateTask("管控区侵入测试", "");
            _taskId = task.Id;

            _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
            _scenario.SaveTarget(_taskId, new TargetConfig
            {
                GroupId = "default",
                TargetType = (int)TargetType.Electric,
                PowerType = (int)PowerType.Electric,
                Quantity = 1,
                TypicalSpeed = 300,
                TypicalAltitude = 300,
            });
            _scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
                new List<Waypoint>
                {
                    new Waypoint { PosX = 0, PosY = 300, PosZ = 0, Speed = 300 },
                    new Waypoint { PosX = 3000, PosY = 300, PosZ = 0, Speed = 300 },
                });
            _scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
            _scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
            _scenario.SaveControlZones(_taskId, new List<Models.ControlZone>
            {
                new Models.ControlZone
                {
                    Name = "禁飞区",
                    VerticesJson = "[{\"X\":1200,\"Y\":0,\"Z\":-200},{\"X\":1800,\"Y\":0,\"Z\":-200},{\"X\":1800,\"Y\":0,\"Z\":200},{\"X\":1200,\"Y\":0,\"Z\":200}]",
                    MinAltitude = 0, MaxAltitude = 1000,
                },
            });

            var eng = RunSimulation(400);
            Assert.Equal(DroneStatus.ZoneIntruded, eng.Drones[0].Status);
        }

        // ═══════════════════════════════════════════════
        // 场景 3：活塞式 200km/h，惰性气体拦截
        // ═══════════════════════════════════════════════

        [Fact]
        public void Scenario_Piston_InertGasIntercept()
        {
            var task = _scenario.CreateTask("拦截活塞式无人机", "");
            _taskId = task.Id;

            _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 3, WindDirection = (int)WindDirection.W });
            _scenario.SaveTarget(_taskId, new TargetConfig
            {
                GroupId = "default", TargetType = (int)TargetType.Piston,
                PowerType = (int)PowerType.Piston,
                Quantity = 1,  // 1 架
                TypicalSpeed = 200, TypicalAltitude = 500,
            });
            _scenario.SaveRoute(_taskId, "default", new RoutePlan
            {
                FormationMode = (int)FormationMode.Formation,
                LateralSpacing = 50,
                LateralCount = 1,
                LongitudinalCount = 1,
            },
                new List<Waypoint>
                {
                    new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 },
                    new Waypoint { PosX = 20000, PosY = 500, PosZ = 0, Speed = 200 },
                });
            _scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>
            {
                MakeEquipment(DefaultFireUnits.GetById("ground-light"), AerosolType.InertGas, 1, 5000, 0, 50),
            });
            _scenario.SaveCloudDispersal(_taskId, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 });

            var eng = RunSimulation(8000);
            var launched = eng.Events.Count(e => e.Type == SimEventType.MunitionLaunched);
            var clouds = eng.Events.Count(e => e.Type == SimEventType.CloudGenerated);

            var times = eng.Events.Where(e => e.Type == SimEventType.MunitionLaunched).Select(e => e.OccurredAt).OrderBy(t => t).ToList();
            var timeInfo = $"times=[{times.First():F2}..{times.Last():F2}] span={times.Last()-times.First():F2}s";

            var msg = $"Piston: launched={launched} clouds={clouds} drones={eng.Drones.Count} {timeInfo}";
            foreach (var d in eng.Drones)
                msg += $" [{d.Status} hp={d.Hp:F2}]";
            VerifyAndExportReport(eng, eng.Drones[0]);
            System.IO.File.WriteAllText(@"C:\Users\Tellme\Desktop\test_piston.txt", msg);
            Assert.True(launched > 0, msg);
            Assert.True(eng.Drones.All(d => d.Status == DroneStatus.Destroyed), msg);
        }

        // ═══════════════════════════════════════════════
        // 场景 4：喷气发动机 → 算法推荐活性材料
        // ═══════════════════════════════════════════════

        [Fact]
        public void Scenario_Jet_ActiveMaterialIntercept()
        {
            var task = _scenario.CreateTask("拦截喷气式无人机", "");
            _taskId = task.Id;

            _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
            _scenario.SaveTarget(_taskId, new TargetConfig
            {
                GroupId = "default",
                TargetType = (int)TargetType.HighSpeed,
                PowerType = (int)PowerType.Jet,
                Quantity = 1,
                TypicalSpeed = 500,
                TypicalAltitude = 800,
            });
            _scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
                new List<Waypoint>
                {
                    new Waypoint { PosX = 0, PosY = 800, PosZ = 0, Speed = 500 },
                    new Waypoint { PosX = 30000, PosY = 800, PosZ = 0, Speed = 500 },
                });

            _scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>
            {
                MakeEquipment(DefaultFireUnits.GetById("ground-standard"), AerosolType.ActiveMaterial, 1, 8000, 0, 50),
            });
            _scenario.SaveCloudDispersal(_taskId, new CloudDispersal { AerosolType = (int)AerosolType.ActiveMaterial, DisperseHeight = 800 });

            var eng = RunSimulation(8000);
            var drone = eng.Drones[0];
            var launched = eng.Events.Count(e => e.Type == SimEventType.MunitionLaunched);
            var clouds = eng.Events.Count(e => e.Type == SimEventType.CloudGenerated);

            var msg = $"Jet: launched={launched} clouds={clouds} status={drone.Status} hp={drone.Hp:F2}";
            System.IO.File.WriteAllText(@"C:\Users\Tellme\Desktop\test_jet.txt", msg);
            VerifyAndExportReport(eng, drone);
            Assert.True(launched > 0, msg);
            Assert.Equal(DroneStatus.Destroyed, drone.Status);
        }
        // ═══════════════════════════════════════════════
        // 场景 5：空基平台 — 基于推荐方案，平台改为空基
        // ═══════════════════════════════════════════════

        [Fact]
        public void Scenario_AirBased_PlatformFliesAndDrops()
        {
            var task = _scenario.CreateTask("空基平台拦截测试", "");
            _taskId = task.Id;

            _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
            _scenario.SaveTarget(_taskId, new TargetConfig
            {
                GroupId = "default",
                TargetType = (int)TargetType.Piston,
                PowerType = (int)PowerType.Piston,
                Quantity = 1,
                TypicalSpeed = 300,
                TypicalAltitude = 500,
            });
            _scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
                new List<Waypoint>
                {
                    new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 300 },
                    new Waypoint { PosX = 15000, PosY = 500, PosZ = 0, Speed = 300 },
                });

            // 部署空基平台单元
            _scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>
            {
                MakeEquipment(DefaultFireUnits.GetById("air-standard"), AerosolType.InertGas, 4, 6500, 2500, 0),
            });
            _scenario.SaveCloudDispersal(_taskId, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 });

            // 验证：空基 PlatformType 正确持久化
            var savedAir = _scenario.GetTaskDetail(_taskId)!;
            Assert.All(savedAir.Equipment, eq => Assert.Equal((int?)0, eq.PlatformType));

            var eng = RunSimulation(8000);

            // 诊断：空基发射事件应有"空基"描述
            Assert.All(eng.Events.Where(e => e.Type == SimEventType.MunitionLaunched),
                e => Assert.Contains("空基", e.Description));

            // 验证引擎内 PlatformType
            Assert.NotEmpty(eng.Events);
            Assert.All(eng.Events.Where(e => e.Type == SimEventType.MunitionLaunched),
                e => Assert.Contains("空基", e.Description));
            var drone = eng.Drones[0];
            var launched = eng.Events.Count(e => e.Type == SimEventType.MunitionLaunched);
            var clouds = eng.Events.Count(e => e.Type == SimEventType.CloudGenerated);

            var msg = $"AirBased: launched={launched} clouds={clouds} status={drone.Status} hp={drone.Hp:F2}";
            System.IO.File.WriteAllText(@"C:\Users\Tellme\Desktop\test_air.txt", msg);
            VerifyAndExportReport(eng, drone);
            Assert.True(launched > 0, msg);
            Assert.Equal(DroneStatus.Destroyed, drone.Status);
        }
    }
}
