using System.Collections.Generic;
using UnityEngine;

namespace FishROV.AvoidanceBenchmark
{
    public sealed class AvoidancePerfBootstrap : MonoBehaviour
    {
        private static readonly int[] CountPresets = { 100, 300, 500, 800, 1000, 1500, 2000, 3000, 5000 };

        [SerializeField] private AvoidanceFrameworkKind framework = AvoidanceFrameworkKind.NoAvoidance;
        [SerializeField] private AvoidanceScenarioKind scenario = AvoidanceScenarioKind.FreeSwim;
        [SerializeField] private AvoidanceDimensionMode dimensionMode = AvoidanceDimensionMode.XZ25D;
        [SerializeField] private BenchmarkViewMode viewMode = BenchmarkViewMode.Overview;
        [SerializeField] private int fishCount = 300;
        [SerializeField] private Material fishMaterial;
        [SerializeField] private float fishMaxSpeed = 2.4f;
        [SerializeField] private float baitDemoMaxSpeed = 4.2f;
        [SerializeField] private float speedMultiplier = 1f;
        [SerializeField] private float timeScale = 1f;
        [SerializeField] private float hookDropHeight = 10f;
        [SerializeField] private float hookDropDuration = 1.5f;

        private readonly List<BenchmarkAgent> agents = new List<BenchmarkAgent>(512);
        private readonly AvoidanceScenarioController scenarioController = new AvoidanceScenarioController();
        private readonly AvoidanceMetricsRecorder metrics = new AvoidanceMetricsRecorder();
        private readonly AvoidanceCollisionProbe collisionProbe = new AvoidanceCollisionProbe();
        private readonly AvoidanceBenchmarkLogWriter logWriter = new AvoidanceBenchmarkLogWriter();
        private AvoidanceBenchmarkConfig config;
        private IAvoidanceAdapter adapter;
        private Transform agentRoot;
        private Transform baitMarker;
        private Camera benchmarkCamera;
        private bool paused;
        private int pendingFishCount;
        private MotionStats motionStats;
        private bool hookDropping;
        private float hookDropElapsed;
        private Vector3 hookDropStart;
        private Vector3 hookDropTarget;

        private void Start()
        {
            QualitySettings.vSyncCount = 0;
            Application.targetFrameRate = -1;
            ApplyTimeScale();
            pendingFishCount = fishCount;
            metrics.Start();
            EnsureCameraAndLight();
            EnsureBaitMarker();
            ResetBenchmark();
        }

        private void Update()
        {
            metrics.RecordFrame(Time.unscaledDeltaTime);
            UpdateCamera();

            if (paused || adapter == null)
            {
                return;
            }

            metrics.BeginSimulation();
            UpdateHookDrop(Time.deltaTime);
            ApplyRuntimeSpeed();
            scenarioController.Tick(Time.deltaTime);
            foreach (var agent in agents)
            {
                var velocity = scenarioController.GetPreferredVelocity(agent, Time.deltaTime);
                adapter.SetPreferredVelocity(agent, velocity);
            }

            adapter.Tick(Time.deltaTime);
            foreach (var agent in agents)
            {
                agent.Integrate(Time.deltaTime, config.DimensionMode, config.ArenaRadius);
            }

            motionStats = CalculateMotionStats();
            collisionProbe.Update(agents, config.DimensionMode, Time.unscaledDeltaTime);
            metrics.EndSimulation();
            logWriter.Record(metrics, collisionProbe, motionStats, agents.Count);
        }

        private void OnGUI()
        {
            GUILayout.BeginArea(new Rect(12, 12, 430, Screen.height - 24), GUI.skin.box);
            GUILayout.Label("FishROV 本地避障压测");
            GUILayout.Label($"当前方案：{adapter?.Name ?? "未初始化"}");
            GUILayout.Label($"状态：{adapter?.Status ?? "未初始化"}");
            GUILayout.Label($"鱼数量：{fishCount} | 对象总数：{agents.Count}");
            GUILayout.Label($"帧率：平均 {metrics.AverageFps:F1} | 1% Low {metrics.OnePercentLowFps:F1}");
            GUILayout.Label($"模拟耗时：{metrics.SimulationMs:F2} ms | GC {metrics.GcAllocBytes / 1024f:F1} KB");
            GUILayout.Label($"速度：极速设定 {(config?.FishSpeed ?? fishMaxSpeed):F1} | 实测峰值 {motionStats.MaxSpeed:F2} | 平均 {motionStats.AverageSpeed:F2}");
            GUILayout.Label($"加速度：峰值 {motionStats.MaxAcceleration:F2} | 平均 {motionStats.AverageAcceleration:F2}");
            GUILayout.Label($"调试倍率：速度 x{speedMultiplier:F2} | 时间 x{timeScale:F2}");
            GUILayout.Label(hookDropping
                ? $"鱼钩下落中：{Mathf.Clamp01(hookDropElapsed / Mathf.Max(0.01f, hookDropDuration)) * 100f:F0}%"
                : "鱼钩状态：未下落");
            GUILayout.Label($"重叠：XZ {collisionProbe.HorizontalOverlapPairs} | 3D {collisionProbe.SpatialOverlapPairs} | 最近间距 {collisionProbe.MinimumClearance:F2}");
            GUILayout.Label($"帧率限制：不锁帧，vSync={QualitySettings.vSyncCount}，target={Application.targetFrameRate}");
            GUILayout.Label($"日志样本：{logWriter.SampleCount} | {logWriter.LastExportPath}");

            GUILayout.Space(8);
            if (GUILayout.Button("投放鱼钩：下落后惊扰争饵", GUILayout.Height(28)))
            {
                StartBaitScrambleDemo();
            }

            GUILayout.Space(8);
            DrawRuntimeTuningControls();

            GUILayout.Space(8);
            DrawEnumButtons("避障方案", framework, value =>
            {
                framework = value;
                ResetBenchmark();
            });
            DrawEnumButtons("测试场景", scenario, value =>
            {
                scenario = value;
                ResetBenchmark();
            });
            DrawEnumButtons("计算维度", dimensionMode, value =>
            {
                dimensionMode = value;
                ResetBenchmark();
            });
            DrawEnumButtons("观察视角", viewMode, value => viewMode = value);

            GUILayout.Space(8);
            GUILayout.Label($"鱼数量：{pendingFishCount}");
            GUILayout.BeginHorizontal();
            foreach (var preset in CountPresets)
            {
                if (GUILayout.Button(preset.ToString(), GUILayout.Height(24)))
                {
                    pendingFishCount = preset;
                    fishCount = preset;
                    ResetBenchmark();
                }
            }
            GUILayout.EndHorizontal();

            pendingFishCount = Mathf.RoundToInt(GUILayout.HorizontalSlider(pendingFishCount, 10, 5000));

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("应用数量"))
            {
                fishCount = pendingFishCount;
                ResetBenchmark();
            }

            if (GUILayout.Button(paused ? "继续" : "暂停"))
            {
                paused = !paused;
            }

            if (GUILayout.Button("重置"))
            {
                ResetBenchmark();
            }

            if (GUILayout.Button("导出日志"))
            {
                logWriter.ExportIfNeeded();
            }
            GUILayout.EndHorizontal();
            GUILayout.EndArea();
        }

        private void OnDestroy()
        {
            Time.timeScale = 1f;
            logWriter.ExportIfNeeded();
            adapter?.Dispose();
            metrics.Dispose();
        }

        private void ResetBenchmark()
        {
            adapter?.Dispose();
            adapter = null;
            logWriter.ExportIfNeeded();

            if (agentRoot != null)
            {
                Destroy(agentRoot.gameObject);
            }

            Random.InitState(13999);
            config = new AvoidanceBenchmarkConfig
            {
                Framework = framework,
                Scenario = scenario,
                DimensionMode = dimensionMode,
                FishCount = fishCount,
                FishSpeed = scenario == AvoidanceScenarioKind.BaitScramble
                    ? Mathf.Max(fishMaxSpeed, baitDemoMaxSpeed)
                    : Mathf.Max(0.1f, fishMaxSpeed),
                UseSchoolProxy = false
            };

            agentRoot = new GameObject("Benchmark Agents").transform;
            agents.Clear();
            adapter = AvoidanceAdapterRegistry.Create(config.Framework);
            adapter.Initialize(config);
            BenchmarkAgent.SetSharedConeMaterial(fishMaterial);

            SpawnFish();
            scenarioController.Reset(config, agents);
            if (scenario == AvoidanceScenarioKind.BaitScramble)
            {
                DropBaitAtSchoolCenter();
            }
            else if (baitMarker != null)
            {
                baitMarker.gameObject.SetActive(false);
            }

            hookDropping = false;
            motionStats = CalculateMotionStats();
            logWriter.StartRun(config, adapter.Name);
        }

        private void DrawRuntimeTuningControls()
        {
            GUILayout.Label($"速度倍率：x{speedMultiplier:F2}");
            speedMultiplier = GUILayout.HorizontalSlider(speedMultiplier, 0.25f, 4f);
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("0.5x"))
            {
                speedMultiplier = 0.5f;
            }

            if (GUILayout.Button("1x"))
            {
                speedMultiplier = 1f;
            }

            if (GUILayout.Button("2x"))
            {
                speedMultiplier = 2f;
            }

            if (GUILayout.Button("4x"))
            {
                speedMultiplier = 4f;
            }

            GUILayout.EndHorizontal();

            GUILayout.Label($"时间缩放：x{timeScale:F2}");
            var newTimeScale = GUILayout.HorizontalSlider(timeScale, 0.1f, 5f);
            if (!Mathf.Approximately(newTimeScale, timeScale))
            {
                timeScale = newTimeScale;
                ApplyTimeScale();
            }

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("0.25x"))
            {
                timeScale = 0.25f;
                ApplyTimeScale();
            }

            if (GUILayout.Button("1x"))
            {
                timeScale = 1f;
                ApplyTimeScale();
            }

            if (GUILayout.Button("2x"))
            {
                timeScale = 2f;
                ApplyTimeScale();
            }

            if (GUILayout.Button("5x"))
            {
                timeScale = 5f;
                ApplyTimeScale();
            }

            GUILayout.EndHorizontal();
        }

        private void ApplyRuntimeSpeed()
        {
            if (config == null)
            {
                return;
            }

            var runtimeMaxSpeed = Mathf.Max(0.01f, config.FishSpeed * Mathf.Max(0.01f, speedMultiplier));
            for (var i = 0; i < agents.Count; i++)
            {
                var agent = agents[i];
                if (agent != null)
                {
                    agent.SetMaxSpeed(runtimeMaxSpeed);
                }
            }
        }

        private void ApplyTimeScale()
        {
            Time.timeScale = Mathf.Clamp(timeScale, 0.05f, 8f);
        }

        private void SpawnFish()
        {
            for (var i = 0; i < config.FishCount; i++)
            {
                var spawn = new BenchmarkAgentSpawnInfo
                {
                    Position = SpawnPosition(i, config.FishCount, config.ArenaRadius * 0.5f),
                    Radius = config.FishRadius,
                    Height = config.FishRadius * 2f,
                    MaxSpeed = config.FishSpeed,
                    Layer = 1,
                    CollidesWith = 1 | 2 | 4,
                    Priority = 0.2f,
                    Role = BenchmarkAgentRole.Fish,
                    GroupId = i
                };
                agents.Add(adapter.CreateAgent(spawn, agentRoot));
            }
        }

        private Vector3 SpawnPosition(int index, int total, float radius)
        {
            if (scenario == AvoidanceScenarioKind.CrossFlow)
            {
                var side = index % 2 == 0 ? -1f : 1f;
                return new Vector3(side * radius, LayerY(index), Random.Range(-radius * 0.7f, radius * 0.7f));
            }

            if (scenario == AvoidanceScenarioKind.DenseCircleStress)
            {
                var angle = index * Mathf.PI * 2f / Mathf.Max(1, total);
                return new Vector3(Mathf.Cos(angle) * radius, LayerY(index), Mathf.Sin(angle) * radius);
            }

            if (scenario == AvoidanceScenarioKind.BaitScramble)
            {
                var angle = index * Mathf.PI * 2f / Mathf.Max(1, total);
                var band = 0.55f + (index % 7) * 0.055f;
                var jitter = Random.insideUnitCircle * radius * 0.12f;
                return new Vector3(
                    Mathf.Cos(angle) * radius * band + jitter.x,
                    LayerY(index),
                    Mathf.Sin(angle) * radius * band + jitter.y);
            }

            var point = Random.insideUnitCircle * radius;
            return new Vector3(point.x, LayerY(index), point.y);
        }

        private float LayerY(int index)
        {
            switch (dimensionMode)
            {
                case AvoidanceDimensionMode.XZ2D:
                    return 0f;
                case AvoidanceDimensionMode.XZ25D:
                    return (index % 5 - 2) * 0.22f;
                case AvoidanceDimensionMode.XYZ3D:
                    return Random.Range(-4f, 4f);
                default:
                    return 0f;
            }
        }

        private void StartBaitScrambleDemo()
        {
            scenario = AvoidanceScenarioKind.BaitScramble;
            StartHookDropAtSchoolCenter();
        }

        private void StartHookDropAtSchoolCenter()
        {
            var position = CalculateAgentCenter();
            position.y = 0f;
            hookDropTarget = position;
            hookDropStart = position + Vector3.up * Mathf.Max(0f, hookDropHeight);
            hookDropElapsed = 0f;
            hookDropping = true;

            if (baitMarker != null)
            {
                baitMarker.position = hookDropStart;
                baitMarker.gameObject.SetActive(true);
            }
        }

        private void DropBaitAtSchoolCenter()
        {
            var position = CalculateAgentCenter();
            position.y = 0f;
            DropBaitAtPosition(position);
        }

        private void DropBaitAtPosition(Vector3 position)
        {
            if (config != null)
            {
                config.Scenario = AvoidanceScenarioKind.BaitScramble;
                config.FishSpeed = Mathf.Max(config.FishSpeed, baitDemoMaxSpeed);
            }

            scenarioController.DropBait(position);
            if (baitMarker != null)
            {
                baitMarker.position = position;
                baitMarker.gameObject.SetActive(true);
            }
        }

        private void UpdateHookDrop(float deltaTime)
        {
            if (!hookDropping)
            {
                return;
            }

            hookDropElapsed += deltaTime;
            var duration = Mathf.Max(0.01f, hookDropDuration);
            var t = Mathf.Clamp01(hookDropElapsed / duration);
            var eased = 1f - (1f - t) * (1f - t);
            var position = Vector3.Lerp(hookDropStart, hookDropTarget, eased);

            if (baitMarker != null)
            {
                baitMarker.position = position;
                baitMarker.gameObject.SetActive(true);
            }

            if (t >= 1f)
            {
                hookDropping = false;
                DropBaitAtPosition(hookDropTarget);
            }
        }

        private void EnsureCameraAndLight()
        {
            benchmarkCamera = Camera.main;
            if (benchmarkCamera == null)
            {
                var cameraGo = new GameObject("Main Camera");
                cameraGo.tag = "MainCamera";
                benchmarkCamera = cameraGo.AddComponent<Camera>();
                benchmarkCamera.clearFlags = CameraClearFlags.Skybox;
                benchmarkCamera.fieldOfView = 55f;
            }

            if (FindObjectOfType<Light>() == null)
            {
                var lightGo = new GameObject("Directional Light");
                var light = lightGo.AddComponent<Light>();
                light.type = LightType.Directional;
                light.intensity = 1.2f;
                lightGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
            }
        }

        private void EnsureBaitMarker()
        {
            if (baitMarker != null)
            {
                return;
            }

            var marker = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            marker.name = "Benchmark Bait Marker";
            marker.transform.localScale = Vector3.one * 0.55f;
            var renderer = marker.GetComponent<Renderer>();
            if (renderer != null && fishMaterial != null)
            {
                renderer.sharedMaterial = fishMaterial;
            }

            var collider = marker.GetComponent<Collider>();
            if (collider != null)
            {
                collider.enabled = false;
            }

            baitMarker = marker.transform;
            marker.SetActive(false);
        }

        private void UpdateCamera()
        {
            if (benchmarkCamera == null || config == null)
            {
                return;
            }

            var radius = config.ArenaRadius;
            Vector3 position;
            Quaternion rotation;

            switch (viewMode)
            {
                case BenchmarkViewMode.TopDown:
                    position = new Vector3(0f, radius * 2.1f, 0.1f);
                    rotation = Quaternion.Euler(89f, 0f, 0f);
                    break;
                case BenchmarkViewMode.Side:
                    position = new Vector3(0f, radius * 0.45f, -radius * 1.9f);
                    rotation = Quaternion.Euler(12f, 0f, 0f);
                    break;
                case BenchmarkViewMode.FollowSchool:
                    var center = CalculateAgentCenter();
                    position = center + new Vector3(0f, radius * 0.65f, -radius);
                    rotation = Quaternion.LookRotation(center - position, Vector3.up);
                    break;
                case BenchmarkViewMode.Overview:
                default:
                    position = new Vector3(0f, radius * 1.1f, -radius * 1.45f);
                    rotation = Quaternion.Euler(42f, 0f, 0f);
                    break;
            }

            benchmarkCamera.transform.SetPositionAndRotation(position, rotation);
        }

        private Vector3 CalculateAgentCenter()
        {
            if (agents.Count == 0)
            {
                return Vector3.zero;
            }

            var sum = Vector3.zero;
            foreach (var agent in agents)
            {
                sum += agent.transform.position;
            }

            return sum / agents.Count;
        }

        private MotionStats CalculateMotionStats()
        {
            if (agents.Count == 0)
            {
                return default;
            }

            var speedSum = 0f;
            var accelerationSum = 0f;
            var maxSpeedValue = 0f;
            var maxAccelerationValue = 0f;
            for (var i = 0; i < agents.Count; i++)
            {
                var agent = agents[i];
                if (agent == null)
                {
                    continue;
                }

                speedSum += agent.CurrentSpeed;
                accelerationSum += agent.CurrentAcceleration;
                maxSpeedValue = Mathf.Max(maxSpeedValue, agent.PeakSpeed);
                maxAccelerationValue = Mathf.Max(maxAccelerationValue, agent.PeakAcceleration);
            }

            return new MotionStats
            {
                AverageSpeed = speedSum / agents.Count,
                MaxSpeed = maxSpeedValue,
                AverageAcceleration = accelerationSum / agents.Count,
                MaxAcceleration = maxAccelerationValue
            };
        }

        private static void DrawEnumButtons<T>(string label, T current, System.Action<T> onChanged) where T : System.Enum
        {
            GUILayout.Label(label);
            GUILayout.BeginHorizontal();
            foreach (T value in System.Enum.GetValues(typeof(T)))
            {
                GUI.enabled = !EqualityComparer<T>.Default.Equals(value, current);
                if (GUILayout.Button(GetDisplayName(value), GUILayout.Height(24)))
                {
                    onChanged(value);
                }
            }
            GUI.enabled = true;
            GUILayout.EndHorizontal();
        }

        private static string GetDisplayName<T>(T value) where T : System.Enum
        {
            var boxed = (object)value;
            switch (boxed)
            {
                case AvoidanceFrameworkKind.NoAvoidance:
                    return "无避障";
                case AvoidanceFrameworkKind.Rvo2:
                    return "RVO2";
                case AvoidanceFrameworkKind.AstarProRvo:
                    return "A* Pro RVO";
                case AvoidanceFrameworkKind.AstarProRvoVelocityLike:
                    return "A* Pro RVO Velocity";
                case AvoidanceScenarioKind.FreeSwim:
                    return "自由巡游";
                case AvoidanceScenarioKind.CrossFlow:
                    return "对向穿流";
                case AvoidanceScenarioKind.NarrowPassage:
                    return "狭窄通道";
                case AvoidanceScenarioKind.DenseCircleStress:
                    return "圆阵高压";
                case AvoidanceScenarioKind.BaitScramble:
                    return "鱼钩争饵";
                case AvoidanceDimensionMode.XZ2D:
                    return "2D/XZ";
                case AvoidanceDimensionMode.XZ25D:
                    return "2.5D";
                case AvoidanceDimensionMode.XYZ3D:
                    return "3D/XYZ";
                case BenchmarkViewMode.Overview:
                    return "总览";
                case BenchmarkViewMode.TopDown:
                    return "俯视";
                case BenchmarkViewMode.Side:
                    return "侧视";
                case BenchmarkViewMode.FollowSchool:
                    return "跟随鱼群";
                default:
                    return value.ToString();
            }
        }

        public struct MotionStats
        {
            public float AverageSpeed;
            public float MaxSpeed;
            public float AverageAcceleration;
            public float MaxAcceleration;
        }
    }
}
