using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using UnityEngine;

namespace FishROV.AvoidanceBenchmark
{
    public sealed class AvoidanceBenchmarkLogWriter
    {
        private const float SampleInterval = 0.5f;
        private const int MaxSamples = 4096;

        private readonly List<Sample> samples = new List<Sample>(1024);
        private AvoidanceBenchmarkConfig config;
        private string runName;
        private float runStartTime;
        private float nextSampleTime;
        private bool hasRun;

        public string LastExportPath { get; private set; } = "尚未导出";
        public int SampleCount => samples.Count;

        public void StartRun(AvoidanceBenchmarkConfig benchmarkConfig, string adapterName)
        {
            ExportIfNeeded();

            config = benchmarkConfig.Clone();
            runName = SanitizeFileName($"{DateTime.Now:yyyyMMdd_HHmmss}_{adapterName}_{config.Scenario}_{config.DimensionMode}_{config.FishCount}");
            runStartTime = Time.unscaledTime;
            nextSampleTime = runStartTime;
            samples.Clear();
            hasRun = true;
            LastExportPath = "本轮尚未导出";
        }

        public void Record(
            AvoidanceMetricsRecorder metrics,
            AvoidanceCollisionProbe collisionProbe,
            int agentCount)
        {
            if (!hasRun || Time.unscaledTime < nextSampleTime || samples.Count >= MaxSamples)
            {
                return;
            }

            nextSampleTime += SampleInterval;
            samples.Add(new Sample
            {
                TimeSeconds = Time.unscaledTime - runStartTime,
                AgentCount = agentCount,
                AverageFps = metrics.AverageFps,
                OnePercentLowFps = metrics.OnePercentLowFps,
                SimulationMs = metrics.SimulationMs,
                GcAllocKb = metrics.GcAllocBytes / 1024.0,
                HorizontalOverlapPairs = collisionProbe.HorizontalOverlapPairs,
                SpatialOverlapPairs = collisionProbe.SpatialOverlapPairs,
                MinimumClearance = collisionProbe.MinimumClearance
            });
        }

        public string ExportIfNeeded()
        {
            if (!hasRun || samples.Count == 0)
            {
                return LastExportPath;
            }

            var directory = Path.Combine(Application.persistentDataPath, "AvoidanceBenchmarkLogs");
            Directory.CreateDirectory(directory);
            var path = Path.Combine(directory, runName + ".csv");

            using (var writer = new StreamWriter(path, false))
            {
                writer.WriteLine("framework,scenario,dimension,fishCount,arenaRadius,screenWidth,screenHeight,targetFrameRate,timeSeconds,agentCount,averageFps,onePercentLowFps,simulationMs,gcAllocKb,horizontalOverlapPairs,spatialOverlapPairs,minimumClearance");
                for (var i = 0; i < samples.Count; i++)
                {
                    var sample = samples[i];
                    writer.Write(config.Framework);
                    writer.Write(',');
                    writer.Write(config.Scenario);
                    writer.Write(',');
                    writer.Write(config.DimensionMode);
                    writer.Write(',');
                    writer.Write(config.FishCount);
                    writer.Write(',');
                    WriteFloat(writer, config.ArenaRadius);
                    writer.Write(',');
                    writer.Write(Screen.width);
                    writer.Write(',');
                    writer.Write(Screen.height);
                    writer.Write(',');
                    writer.Write(Application.targetFrameRate);
                    writer.Write(',');
                    WriteFloat(writer, sample.TimeSeconds);
                    writer.Write(',');
                    writer.Write(sample.AgentCount);
                    writer.Write(',');
                    WriteFloat(writer, sample.AverageFps);
                    writer.Write(',');
                    WriteFloat(writer, sample.OnePercentLowFps);
                    writer.Write(',');
                    WriteDouble(writer, sample.SimulationMs);
                    writer.Write(',');
                    WriteDouble(writer, sample.GcAllocKb);
                    writer.Write(',');
                    writer.Write(sample.HorizontalOverlapPairs);
                    writer.Write(',');
                    writer.Write(sample.SpatialOverlapPairs);
                    writer.Write(',');
                    WriteFloat(writer, sample.MinimumClearance);
                    writer.WriteLine();
                }
            }

            LastExportPath = path;
            samples.Clear();
            hasRun = false;
            Debug.Log($"Avoidance benchmark log exported: {path}");
            return LastExportPath;
        }

        private static void WriteFloat(TextWriter writer, float value)
        {
            writer.Write(value.ToString("0.###", CultureInfo.InvariantCulture));
        }

        private static void WriteDouble(TextWriter writer, double value)
        {
            writer.Write(value.ToString("0.###", CultureInfo.InvariantCulture));
        }

        private static string SanitizeFileName(string value)
        {
            foreach (var invalid in Path.GetInvalidFileNameChars())
            {
                value = value.Replace(invalid, '_');
            }

            return value;
        }

        private struct Sample
        {
            public float TimeSeconds;
            public int AgentCount;
            public float AverageFps;
            public float OnePercentLowFps;
            public double SimulationMs;
            public double GcAllocKb;
            public int HorizontalOverlapPairs;
            public int SpatialOverlapPairs;
            public float MinimumClearance;
        }
    }
}
