using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;

namespace FrameDoctor.Runtime.Components
{
    // Optional runtime profiler component for in-game performance monitoring.
    // Attach to a GameObject to capture performance metrics during gameplay.
    public class RuntimeProfiler : MonoBehaviour
    {
        [Header("Capture Settings")]
        [SerializeField]
        [Tooltip("Enable automatic capture on start")]
        private bool _captureOnStart;

        [SerializeField]
        [Tooltip("Maximum number of frames to store")]
        private int _maxFrames = 1000;

        [Header("Thresholds")]
        [SerializeField]
        [Tooltip("Frame time threshold for warnings (ms)")]
        private float _frameTimeWarningThreshold = 33.33f;

        [SerializeField]
        [Tooltip("GC allocation threshold for warnings (bytes)")]
        private long _gcAllocationWarningThreshold = 1024;

        private bool _isCapturing;
        private readonly List<FrameMetrics> _frameHistory = new List<FrameMetrics>();
        private float _lastFrameTime;
        private long _lastTotalAllocatedMemory;

        // Event fired when a frame exceeds the warning threshold.
        public event Action<FrameMetrics> OnFrameWarning;

        public bool IsCapturing => _isCapturing;
        public IReadOnlyList<FrameMetrics> FrameHistory => _frameHistory;

        private void Start()
        {
            if (_captureOnStart)
            {
                StartCapture();
            }
        }

        private void OnEnable()
        {
            // Event subscriptions would go here
        }

        private void OnDisable()
        {
            // Event unsubscriptions would go here
        }

        private void OnDestroy()
        {
            OnFrameWarning = null;
        }

        private void Update()
        {
            if (!_isCapturing)
            {
                return;
            }

            CaptureFrame();
        }

        public void StartCapture()
        {
            if (_isCapturing)
            {
                return;
            }

            _isCapturing = true;
            _frameHistory.Clear();
            _lastFrameTime = Time.realtimeSinceStartup;
            _lastTotalAllocatedMemory = Profiler.GetTotalAllocatedMemoryLong();

            Debug.Log("[FrameDoctor] Runtime capture started");
        }

        public void StopCapture()
        {
            if (!_isCapturing)
            {
                return;
            }

            _isCapturing = false;
            Debug.Log($"[FrameDoctor] Runtime capture stopped. Captured {_frameHistory.Count} frames.");
        }

        public void ClearHistory()
        {
            _frameHistory.Clear();
        }

        public CaptureSummary GetSummary()
        {
            if (_frameHistory.Count == 0)
            {
                return new CaptureSummary();
            }

            var summary = new CaptureSummary
            {
                TotalFrames = _frameHistory.Count,
                AverageFrameTimeMs = 0,
                MaxFrameTimeMs = 0,
                MinFrameTimeMs = float.MaxValue,
                TotalGcAllocatedBytes = 0,
                FramesOverBudget = 0
            };

            foreach (var frame in _frameHistory)
            {
                summary.AverageFrameTimeMs += frame.FrameTimeMs;
                summary.MaxFrameTimeMs = Mathf.Max(summary.MaxFrameTimeMs, frame.FrameTimeMs);
                summary.MinFrameTimeMs = Mathf.Min(summary.MinFrameTimeMs, frame.FrameTimeMs);
                summary.TotalGcAllocatedBytes += frame.GcAllocatedBytes;

                if (frame.FrameTimeMs > _frameTimeWarningThreshold)
                {
                    summary.FramesOverBudget++;
                }
            }

            summary.AverageFrameTimeMs /= _frameHistory.Count;

            return summary;
        }

        private void CaptureFrame()
        {
            var currentTime = Time.realtimeSinceStartup;
            var currentMemory = Profiler.GetTotalAllocatedMemoryLong();

            var metrics = new FrameMetrics
            {
                FrameCount = Time.frameCount,
                DeltaTime = Time.deltaTime,
                FrameTimeMs = (currentTime - _lastFrameTime) * 1000f,
                GcAllocatedBytes = currentMemory - _lastTotalAllocatedMemory,
                TotalMemoryBytes = currentMemory,
                CpuTimeMs = Time.deltaTime * 1000f
            };

            if (metrics.FrameTimeMs > _frameTimeWarningThreshold)
            {
                OnFrameWarning?.Invoke(metrics);
            }

            _frameHistory.Add(metrics);

            while (_frameHistory.Count > _maxFrames)
            {
                _frameHistory.RemoveAt(0);
            }

            _lastFrameTime = currentTime;
            _lastTotalAllocatedMemory = currentMemory;
        }
    }
}
