using System;
using System.Collections.Generic;
using System.IO;
using CounterDrone.Core;
using CounterDrone.Core.Models;
using CounterDrone.Core.Repository;
using CounterDrone.Core.Services;
using SQLite;
using Xunit;
using ScenarioStatus = CounterDrone.Core.Models.ScenarioStatus;

namespace CounterDrone.Core.Tests
{
    public class ScenarioServiceTests : IDisposable
    {
        private readonly string _testDir;
        private readonly SQLiteConnection _db;
        private readonly IScenarioService _service;

        public ScenarioServiceTests()
        {
            _testDir = Path.Combine(Path.GetTempPath(), $"cd_test_{Guid.NewGuid():N}");
            var paths = new TestPathProvider(_testDir);
            var dbManager = new DatabaseManager(paths);
            _db = dbManager.OpenMainDb();
            _service = new ScenarioService(
                new ScenarioRepository(_db),
                new CombatSceneRepository(_db),
                new ControlZoneRepository(_db),
                new ScenarioDroneRepository(_db),
                new ScenarioUnitRepository(_db),
                new CloudDispersalRepository(_db),
                new RoutePlanRepository(_db),
                new WaypointRepository(_db));
        }

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

        // ========== scenario CRUD ==========

        [Fact]
        public void CreateScenario_Valid_ReturnsScenario()
        {
            var scenario = _service.CreateScenario("测试任务", "SIM-20260611-001");

            Assert.NotNull(scenario);
            Assert.Equal("测试任务", scenario.Name);
            Assert.Equal("SIM-20260611-001", scenario.ScenarioNumber);
            Assert.Equal((int)ScenarioStatus.Configuring, scenario.Status);
            Assert.Equal(1, scenario.CurrentStep);
            Assert.NotEmpty(scenario.Id);
        }

        [Fact]
        public void CreateScenario_AutoGeneratesScenarioNumber()
        {
            var scenario = _service.CreateScenario("自动编号", "");
            Assert.StartsWith("SIM-", scenario.ScenarioNumber);
        }

        [Fact]
        public void CreateScenario_EmptyName_Throws()
        {
            Assert.Throws<ArgumentException>(() =>
                _service.CreateScenario("", "SIM-001"));
        }

        [Fact]
        public void CreateScenario_DuplicateNumber_Throws()
        {
            _service.CreateScenario("scenario A", "SIM-001");
            Assert.Throws<ArgumentException>(() =>
                _service.CreateScenario("scenario B", "SIM-001"));
        }

        [Fact]
        public void DeleteScenario_RemovesScenario()
        {
            var scenario = _service.CreateScenario("ToDelete", "");
            _service.DeleteScenario(scenario.Id);

            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Null(detail);
        }

        [Fact]
        public void GetScenarioDetail_ReturnsFullConfig()
        {
            var scenario = _service.CreateScenario("Full Config", "");

            // 保存各步骤配置
            _service.SaveScene(scenario.Id, new CombatScene
            {
                SceneType = (int)SceneType.Mountain,
                WindSpeed = 10.0,
                Temperature = 25.0,
            });

            _service.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("default", 3));

            _service.SaveCloudDispersal(scenario.Id, new CloudDispersal
            {
                AerosolType = (int)AerosolType.ActiveMaterial,
                Duration = 45.0,
            });

            _service.UpdateStep(scenario.Id, 4);

            // 读取完整配置
            var detail = _service.GetScenarioDetail(scenario.Id);

            Assert.Equal("Full Config", detail!.Info.Name);
            Assert.Equal(4, detail!.Info.CurrentStep);
            Assert.Equal((int)SceneType.Mountain, detail!.Scene.SceneType);
            Assert.Equal(10.0, detail!.Scene.WindSpeed);
            Assert.Equal(3, detail!.Drones[0].Quantity);
            Assert.Equal((int)AerosolType.ActiveMaterial, detail!.Cloud.AerosolType);
        }

        // ========== Step Save ==========

        [Fact]
        public void SaveScene_FirstTime_Inserts()
        {
            var scenario = _service.CreateScenario("Scene Test", "");
            _service.SaveScene(scenario.Id, new CombatScene
            {
                WeatherType = (int)WeatherType.Fog,
                Visibility = 2000.0,
                TimeOfDay = "03:00",
            });

            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal((int)WeatherType.Fog, detail!.Scene.WeatherType);
            Assert.Equal(2000.0, detail!.Scene.Visibility);
            Assert.Equal("03:00", detail!.Scene.TimeOfDay);
        }

        [Fact]
        public void SaveScene_SecondTime_Updates()
        {
            var scenario = _service.CreateScenario("Update Scene", "");
            _service.SaveScene(scenario.Id, new CombatScene { WindSpeed = 5.0 });
            _service.SaveScene(scenario.Id, new CombatScene { WindSpeed = 15.0 });

            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal(15.0, detail!.Scene.WindSpeed);
        }

        [Fact]
        public void SaveControlZones_SavesAndRetrieves()
        {
            var scenario = _service.CreateScenario("Zone Test", "");
            var zones = new List<ControlZone>
            {
                new ControlZone { Name = "禁区A", VerticesJson = "[{\"x\":0,\"y\":0,\"z\":0},{\"x\":100,\"y\":0,\"z\":0},{\"x\":100,\"y\":100,\"z\":0}]", MinAltitude = 0, MaxAltitude = 500 },
                new ControlZone { Name = "禁区B", VerticesJson = "[{\"x\":200,\"y\":200,\"z\":0}]", MinAltitude = 100, MaxAltitude = 300 },
            };

            _service.SaveControlZones(scenario.Id, zones);

            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal(2, detail!.ControlZones.Count);
            Assert.Equal("禁区A", detail!.ControlZones[0].Name);
            Assert.Equal(0, detail!.ControlZones[0].OrderIndex);
            Assert.Equal("禁区B", detail!.ControlZones[1].Name);
            Assert.Equal(1, detail!.ControlZones[1].OrderIndex);
        }

        [Fact]
        public void SaveControlZones_ReplaceExisting()
        {
            var scenario = _service.CreateScenario("Zone Replace", "");
            _service.SaveControlZones(scenario.Id, new List<ControlZone>
            {
                new ControlZone { Name = "Old" }
            });
            _service.SaveControlZones(scenario.Id, new List<ControlZone>
            {
                new ControlZone { Name = "New1" },
                new ControlZone { Name = "New2" },
            });

            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal(2, detail!.ControlZones.Count);
        }

        [Fact]
        public void SaveScenarioDrone_SavesCorrectly()
        {
            var scenario = _service.CreateScenario("Drone Test", "");
            _service.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("default", 5));

            var detail = _service.GetScenarioDetail(scenario.Id);
            var target = detail!.Drones[0];
            Assert.Equal((int)DroneType.FixedWing, TestData.DefaultDroneSpec.DroneType);
            Assert.Equal(5, target.Quantity);
            Assert.Equal(200.0, TestData.DefaultDroneSpec.TypicalSpeed);
        }

        [Fact]
        public void SaveDeployment_SavesMultiple()
        {
            var scenario = _service.CreateScenario("Equip Test", "");
            var unit1 = TestData.CreateScenarioUnit(1000, 0, 50, 0, 3);
            var unit2 = TestData.CreateScenarioUnit(2000, 0, 50, 0, 5);
            unit2.Id = Guid.NewGuid().ToString();

            _service.SaveDeployment(scenario.Id, new List<ScenarioUnit> { unit1, unit2 });

            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal(2, detail!.Units.Count);
            Assert.Contains(detail!.Units, u => u.PositionX == 1000);
            Assert.Contains(detail!.Units, u => u.PositionX == 2000);
        }

        [Fact]
        public void SaveRoute_SavesWaypoints()
        {
            var scenario = _service.CreateScenario("Route Test", "");
            var route = new RoutePlan
            {
                FormationMode = (int)FormationMode.Formation,
                LateralSpacing = 60.0,
            };
            var waypoints = new List<Waypoint>
            {
                new Waypoint { PosX = 0, PosY = 0, PosZ = 100, Altitude = 500, Speed = 100 },
                new Waypoint { PosX = 5000, PosY = 0, PosZ = 100, Altitude = 500, Speed = 100 },
                new Waypoint { PosX = 10000, PosY = 0, PosZ = 100, Altitude = 300, Speed = 80 },
            };

            _service.SaveRoute(scenario.Id, "default", route, waypoints);

            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal((int)FormationMode.Formation, detail!.Routes[0].FormationMode);
            Assert.Equal(3, detail!.WaypointGroups["default"].Count);
            Assert.Equal(0, detail!.WaypointGroups["default"][0].OrderIndex);
            Assert.Equal(2, detail!.WaypointGroups["default"][2].OrderIndex);
            Assert.Equal(5000.0, detail!.WaypointGroups["default"][1].PosX);
        }

        // ========== Search & Pagination ==========

        [Fact]
        public void SearchScenarios_KeywordFilter()
        {
            _service.CreateScenario("城市防御演习", "");
            _service.CreateScenario("平原拦截测试", "");
            _service.CreateScenario("海岸边防演练", "");

            var result = _service.SearchScenarios("城市", null, null, 1, 10);
            Assert.Equal(1, result.TotalCount);
            Assert.Equal("城市防御演习", result.Items[0].Name);
        }

        [Fact]
        public void SearchScenarios_NoResults()
        {
            _service.CreateScenario("scenario A", "");
            var result = _service.SearchScenarios("不存在的任务", null, null, 1, 10);
            Assert.Equal(0, result.TotalCount);
        }

        [Fact]
        public void SearchScenarios_Pagination()
        {
            for (int i = 0; i < 15; i++)
                _service.CreateScenario($"scenario {i:D2}", "");

            var page1 = _service.SearchScenarios("", null, null, 1, 5);
            Assert.Equal(21, page1.TotalCount); // 15 + 6 预设想定
            Assert.Equal(5, page1.Items.Count);
            Assert.Equal(5, page1.TotalPages); // 21 ÷ 5 = 5 页

            var page3 = _service.SearchScenarios("", null, null, 3, 5);
            Assert.Equal(5, page3.Items.Count);
        }

        [Fact]
        public void SearchScenarios_DateRangeFilter()
        {
            // All scenarios created today, so date filter should include them
            _service.CreateScenario("Recent scenario", "");

            var yesterday = DateTime.UtcNow.AddDays(-1).ToString("yyyy-MM-dd");
            var tomorrow = DateTime.UtcNow.AddDays(1).ToString("yyyy-MM-dd");

            var result = _service.SearchScenarios("", yesterday, tomorrow, 1, 10);
            Assert.Equal(7, result.TotalCount); // 1 + 6 预设想定

            var oldResult = _service.SearchScenarios("", "2020-01-01", "2020-12-31", 1, 10);
            Assert.Equal(0, oldResult.TotalCount);
        }

        // ========== UpdateStep ==========

        [Fact]
        public void UpdateStep_ValidSteps()
        {
            var scenario = _service.CreateScenario("Step Test", "");
            Assert.Equal(1, scenario.CurrentStep);

            _service.UpdateStep(scenario.Id, 3);
            var detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal(3, detail!.Info.CurrentStep);

            _service.UpdateStep(scenario.Id, 5);
            detail = _service.GetScenarioDetail(scenario.Id);
            Assert.Equal(5, detail!.Info.CurrentStep);
        }

        [Fact]
        public void UpdateStep_ClampedToRange()
        {
            var scenario = _service.CreateScenario("Clamp Test", "");

            _service.UpdateStep(scenario.Id, 0);
            Assert.Equal(1, _service.GetScenarioDetail(scenario.Id)!.Info.CurrentStep);

            _service.UpdateStep(scenario.Id, 10);
            Assert.Equal(5, _service.GetScenarioDetail(scenario.Id)!.Info.CurrentStep);
        }

        // ========== Full Workflow ==========

        [Fact]
        public void FullWorkflow_CreateConfigureSearch()
        {
            // 1. 创建任务
            var scenario = _service.CreateScenario("完整流程测试", "");
            Assert.NotNull(scenario);

            // 2. 步骤1：作战场景
            _service.SaveScene(scenario.Id, new CombatScene
            {
                SceneType = (int)SceneType.Urban,
                WeatherType = (int)WeatherType.Rain,
                WindSpeed = 8.0,
                WindDirection = (int)WindDirection.SE,
                Temperature = 18.0,
                TimeOfDay = "14:00",
            });
            _service.UpdateStep(scenario.Id, 1);

            // 3. 步骤1扩展：管控区域
            _service.SaveControlZones(scenario.Id, new List<ControlZone>
            {
                new ControlZone
                {
                    Name = "核心区",
                    VerticesJson = "[{\"x\":0,\"y\":0,\"z\":0},{\"x\":500,\"y\":0,\"z\":0},{\"x\":500,\"y\":500,\"z\":0},{\"x\":0,\"y\":500,\"z\":0}]",
                    MinAltitude = 0, MaxAltitude = 1000,
                }
            });

            // 4. 步骤2：目标配置
            _service.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("default", 2));
            _service.UpdateStep(scenario.Id, 2);

            // 5. 步骤3：装备部署
            _service.SaveDeployment(scenario.Id, new List<ScenarioUnit>
            {
                TestData.CreateScenarioUnit(0, 0, 50, 0, null),
                TestData.CreateScenarioUnit(800, 0, 50, 0, 2),
            });
            _service.UpdateStep(scenario.Id, 3);

            // 6. 步骤4：云团抛撒
            _service.SaveCloudDispersal(scenario.Id, new CloudDispersal
            {
                AerosolType = (int)AerosolType.ActiveMaterial,
                TriggerMode = (int)TriggerMode.Area,
                Duration = 50.0,
                PositionX = 3000,
                PositionY = 0,
                PositionZ = 300,
            });
            _service.UpdateStep(scenario.Id, 4);

            // 7. 步骤5：航路规划
            _service.SaveRoute(scenario.Id, "default", new RoutePlan
            {
                FormationMode = (int)FormationMode.Swarm,
                LateralSpacing = 30.0,
            }, new List<Waypoint>
            {
                new Waypoint { PosX = 0, PosY = 100, PosZ = 200, Altitude = 600, Speed = 250 },
                new Waypoint { PosX = 10000, PosY = 100, PosZ = 200, Altitude = 600, Speed = 250 },
            });
            _service.UpdateStep(scenario.Id, 5);

            // 8. 验证完整配置
            var detail = _service.GetScenarioDetail(scenario.Id);

            Assert.Equal("完整流程测试", detail!.Info.Name);
            Assert.Equal(5, detail!.Info.CurrentStep);
            Assert.Equal((int)ScenarioStatus.Configuring, detail.Info.Status);

            // 场景
            Assert.Equal((int)SceneType.Urban, detail!.Scene.SceneType);
            Assert.Equal((int)WeatherType.Rain, detail!.Scene.WeatherType);
            Assert.Equal("14:00", detail!.Scene.TimeOfDay);

            // 管控区域
            Assert.Single(detail.ControlZones);
            Assert.Equal("核心区", detail!.ControlZones[0].Name);

            // 目标
            Assert.Single(detail.Drones);
            Assert.Equal(2, detail!.Drones[0].Quantity);
            Assert.Equal(200.0, TestData.DefaultDroneSpec.TypicalSpeed);

            // 装备
            Assert.NotEmpty(detail!.Units);

            // 云团
            Assert.Equal((int)AerosolType.ActiveMaterial, detail!.Cloud.AerosolType);
            Assert.Equal(50.0, detail.Cloud.Duration);

            // 航路
            Assert.Equal((int)FormationMode.Swarm, detail!.Routes[0].FormationMode);
            Assert.Equal(2, detail!.WaypointGroups["default"].Count);
            Assert.Equal(250.0, detail!.WaypointGroups["default"][0].Speed);

            // 9. 搜索验证
            var searchResult = _service.SearchScenarios("完整流程", null, null, 1, 10);
            Assert.Equal(1, searchResult.TotalCount);
        }

        // ═══════════════════════════════════════
        // 探测设备独立 CRUD
        // ═══════════════════════════════════════

        [Fact]
        public void AddDetection_PersistsAndQueryable()
        {
            var scenario = _service.CreateScenario("探测设备测试", "");
            _service.AddDetection(scenario.Id, TestData.CreateDetectionUnit(5000, 0, 0));

            var detections = _service.GetDetections(scenario.Id);
            Assert.Single(detections);
            Assert.Equal((int)EquipmentRole.Detection, detections[0].EquipmentRole);
            Assert.Equal(5000.0, detections[0].PositionX);
            
        }

        [Fact]
        public void GetDetections_FiltersOutLaunchPlatforms()
        {
            var scenario = _service.CreateScenario("探测过滤测试", "");
            // 一个火力单元
            _service.SaveDeployment(scenario.Id, new List<ScenarioUnit>
            {
                TestData.CreateScenarioUnit(1000, 0, 50, 0, null),
            });
            // 两个探测设备（独立添加）
            _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(0, 0, 50, 0, null));
            _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(0, 0, 50, 0, null));

            var detections = _service.GetDetections(scenario.Id);
            Assert.Equal(2, detections.Count); // 只有探测设备，不含火力单元
        }

        [Fact]
        public void DeleteDetection_RemovesOnlyOne()
        {
            var scenario = _service.CreateScenario("探测删除测试", "");
            var det1 = TestData.CreateDetectionUnit(3000, 0, 0);
            var det2 = TestData.CreateDetectionUnit(5000, 0, 0);
            _service.AddDetection(scenario.Id, det1);
            _service.AddDetection(scenario.Id, det2);

            _service.DeleteDetection(det2.Id);

            var detections = _service.GetDetections(scenario.Id);
            Assert.Single(detections);
            Assert.Equal(3000.0, detections[0].PositionX);
        }

        [Fact]
        public void GetScenarioDetail_IncludesDetections()
        {
            var scenario = _service.CreateScenario("想定含探测", "");
            _service.SaveDeployment(scenario.Id, new List<ScenarioUnit>
            {
                TestData.CreateScenarioUnit(0, 0, 50, 0, null),
            });
            _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(0, 0, 50, 0, null));

            var detail = _service.GetScenarioDetail(scenario.Id);
            // Equipment 包含火力单元 + 探测设备
            Assert.NotEmpty(detail!.Units);
            Assert.Contains(detail!.Units, e => e.EquipmentRole == (int)EquipmentRole.Detection);
            Assert.Contains(detail!.Units, e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform);
        }
    }
}
