#nullable enable
using System;
using System.Collections.Generic;

namespace IGP.UnitySDK.Core
{
    internal static class IGPNetworkAnomalyThresholds
    {
        public const int LargeReliablePayloadBytes = 16 * 1024;
        public const int HighLatencyMs = 200;
        public const int HighLatencyRecoveryMs = 150;
        public const float HighDatagramsPerSecond = 1000f;
        public const float HighDatagramsPerSecondRecovery = 750f;
        public const float HighDatagramBytesPerSecond = 1024f * 1024f;
        public const float HighDatagramBytesPerSecondRecovery = 768f * 1024f;
    }

    internal sealed class IGPNetworkAnomalyLogLimiter
    {
        internal const float DefaultRepeatIntervalSeconds = 30f;
        internal const float DefaultRecoveryQuietPeriodSeconds = 5f;

        private sealed class State
        {
            public bool Active;
            public float StartedAt;
            public float LastWarningAt;
            public float NormalSince = -1f;
            public int SamplesSinceLastWarning;
        }

        private readonly Dictionary<string, State> states =
            new Dictionary<string, State>(StringComparer.Ordinal);
        private readonly float repeatIntervalSeconds;
        private readonly float recoveryQuietPeriodSeconds;

        public IGPNetworkAnomalyLogLimiter(
            float repeatIntervalSeconds = DefaultRepeatIntervalSeconds,
            float recoveryQuietPeriodSeconds = DefaultRecoveryQuietPeriodSeconds)
        {
            this.repeatIntervalSeconds = Math.Max(1f, repeatIntervalSeconds);
            this.recoveryQuietPeriodSeconds = Math.Max(0f, recoveryQuietPeriodSeconds);
        }

        public void ObserveEvent(
            string kind,
            float now,
            Func<string> detailFactory,
            Action<string> warning)
        {
            var state = GetState(kind);
            state.SamplesSinceLastWarning += 1;

            if (!state.Active)
            {
                state.Active = true;
                state.StartedAt = now;
                state.LastWarningAt = now;
                state.SamplesSinceLastWarning = 0;
                warning(BuildMessage(kind, "started", detailFactory(), 1, 0f));
                return;
            }

            if (now - state.LastWarningAt < repeatIntervalSeconds)
            {
                return;
            }

            int occurrences = state.SamplesSinceLastWarning;
            state.LastWarningAt = now;
            state.SamplesSinceLastWarning = 0;
            warning(BuildMessage(kind, "ongoing", detailFactory(), occurrences, now - state.StartedAt));
        }

        public void ObserveContinuous(
            string kind,
            bool abnormal,
            bool recovered,
            float now,
            Func<string> detailFactory,
            Action<string> warning,
            Action<string> info)
        {
            var state = GetState(kind);
            if (abnormal)
            {
                state.NormalSince = -1f;
                state.SamplesSinceLastWarning += 1;
                if (!state.Active)
                {
                    state.Active = true;
                    state.StartedAt = now;
                    state.LastWarningAt = now;
                    state.SamplesSinceLastWarning = 0;
                    warning(BuildMessage(kind, "started", detailFactory(), 1, 0f));
                }
                else if (now - state.LastWarningAt >= repeatIntervalSeconds)
                {
                    int samples = state.SamplesSinceLastWarning;
                    state.LastWarningAt = now;
                    state.SamplesSinceLastWarning = 0;
                    warning(BuildMessage(kind, "ongoing", detailFactory(), samples, now - state.StartedAt));
                }

                return;
            }

            if (!state.Active)
            {
                return;
            }

            if (!recovered)
            {
                state.NormalSince = -1f;
                return;
            }

            if (state.NormalSince < 0f)
            {
                state.NormalSince = now;
            }

            if (now - state.NormalSince < recoveryQuietPeriodSeconds)
            {
                return;
            }

            info(BuildMessage(kind, "recovered", detailFactory(), 0, now - state.StartedAt));
            states.Remove(kind);
        }

        public void Reset()
        {
            states.Clear();
        }

        private State GetState(string kind)
        {
            if (!states.TryGetValue(kind, out var state))
            {
                state = new State();
                states[kind] = state;
            }

            return state;
        }

        private static string BuildMessage(
            string kind,
            string phase,
            string details,
            int samples,
            float durationSeconds)
        {
            string sampleDetail = samples > 0 ? $" samplesSinceLast={samples}" : string.Empty;
            string durationDetail = durationSeconds > 0f ? $" durationSeconds={durationSeconds:F1}" : string.Empty;
            return $"event=network-anomaly kind={kind} phase={phase}{sampleDetail}{durationDetail} " +
                   $"action=observe-only {details}";
        }
    }
}
