using System;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace AIRuntime.Editor
{
    /// <summary>One editor-owned session. Views may attach/detach without cancelling runs.</summary>
    [InitializeOnLoad]
    internal static class AIRuntimeKanbanScheduler
    {
        private static AIRuntimeKanbanSession _session;
        private static double _nextTick;
        private static volatile bool _wakeRequested;
        private static string PathKey => "AIRuntime.Kanban.ServiceBoard." + Application.dataPath;

        static AIRuntimeKanbanScheduler()
        {
            EditorApplication.update += Tick;
            AssemblyReloadEvents.beforeAssemblyReload += PrepareForReload;
            EditorApplication.quitting += Shutdown;
            EditorApplication.delayCall += Restore;
        }

        internal static AIRuntimeKanbanSession Session => GetSession();

        internal static AIRuntimeKanbanSession GetSession(string boardPath = null)
        {
            if (_session != null) return _session;
            foreach (var existing in Resources.FindObjectsOfTypeAll<AIRuntimeKanbanSession>())
            {
                if (!existing.IsServiceSession) continue;
                _session = existing;
                Wake();
                return _session;
            }
            // SessionState also covers reloads without a visible panel. Batch tests
            // never restore the user's board or enable paid provider work implicitly.
            string path = boardPath;
            if (string.IsNullOrWhiteSpace(path))
                path = SessionState.GetString(PathKey, string.Empty);
            if (string.IsNullOrWhiteSpace(path) && !Application.isBatchMode)
                path = EditorPrefs.GetString(PathKey, string.Empty);
            _session = AIRuntimeKanbanSession.CreateServiceSession(path);
            _session.IsServiceSession = true;
            _session.hideFlags = HideFlags.HideAndDontSave;
            RememberPath(_session.BoardPath);
            Wake();
            return _session;
        }

        private static void Restore()
        {
            if (Application.isBatchMode || !string.IsNullOrWhiteSpace(AIRuntimeKanbanSession.BoardPathOverrideForTests))
                return;
            string path = SessionState.GetString(PathKey, EditorPrefs.GetString(PathKey, string.Empty));
            if (!string.IsNullOrWhiteSpace(path) && File.Exists(path)) GetSession(path);
        }

        internal static void RememberPath(string path)
        {
            if (_session == null || string.IsNullOrWhiteSpace(path)) return;
            SessionState.SetString(PathKey, path);
            if (!Application.isBatchMode) EditorPrefs.SetString(PathKey, path);
        }

        internal static void Wake() => _wakeRequested = true;

        private static void Tick()
        {
            if (_session == null || (!_wakeRequested && EditorApplication.timeSinceStartup < _nextTick)) return;
            _wakeRequested = false;
            _nextTick = EditorApplication.timeSinceStartup + 1;
            try
            {
                _session.PumpAgentQueue();
                _session.OnInspectorUpdate();
                RememberPath(_session.BoardPath);
                _nextTick = EditorApplication.timeSinceStartup + _session.NextPollSeconds;
            }
            catch (Exception exception)
            {
                _session.StopAfterSchedulerFailure(exception);
            }
        }

        private static void PrepareForReload()
        {
            if (_session != null) _session.PrepareForAssemblyReload();
        }

        private static void Shutdown()
        {
            if (_session == null) return;
            _session.PrepareForAssemblyReload();
            UnityEngine.Object.DestroyImmediate(_session);
            _session = null;
        }
    }
}
