#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEngine;

public partial class OctopusSDK
{
    /// <summary>
    /// Editor-only stand-in for the native bridge. Every wrapper routes here in-Editor.
    /// Records calls, drives the overlay, and resolves async operations so nothing hangs.
    /// </summary>
    internal static partial class MockBackend
    {
        internal static void Initialize(string apiKey, ConnectionMode mode)
        {
            // An OctopusMockSettings asset, if present, seeds the toggles. Otherwise keep
            // whatever the host set in code (OctopusSDK.Mock.Enabled / .ShowOverlay) — defaults
            // are enabled + overlay on. Set those BEFORE Initialize for them to take effect.
            var settings = OctopusMockSettings.Instance;
            if (settings != null)
            {
                Mock.Enabled = settings.EnabledByDefault;
                Mock.ShowOverlay = settings.ShowOverlay;
            }
            // Note: when Enabled is false, Record() suppresses, so this Initialize
            // call won't appear in Mock.Calls — intentional (mirrors a no-op SDK).
            LifecycleInitialized = true;
            Mock.Record("Initialize", apiKey, mode.Mode);
            InitializeProfileMock(mode);
            // Create the overlay whenever mock is on; ShowOverlay (checked in OnGUI) governs
            // whether it actually draws, so it can be toggled live during Play.
            if (Mock.Enabled) OctopusMockOverlay.EnsureExists();
        }

        internal static void Open(OctopusNotification notification, OctopusNavigationMode? navigationMode)
        {
            Mock.Record("Open", notification?.DeepLink ?? "", navigationMode);
            if (Mock.Enabled) Mock.CurrentScreen = "Main feed";
        }

        internal static void OpenGroup(string groupId, OctopusNavigationMode? navigationMode)
        {
            Mock.Record("OpenGroup", groupId ?? "", navigationMode);
            if (Mock.Enabled) Mock.CurrentScreen = $"Group {groupId}";
        }

        internal static void OpenPost(string postId, OctopusNavigationMode? navigationMode)
        {
            Mock.Record("OpenPost", postId ?? "", navigationMode);
            if (Mock.Enabled) { Mock.LastOpenedPost = postId; Mock.CurrentScreen = $"Post {postId}"; }
        }

        internal static void OpenCreatePost(OctopusPrefilledPost prefilled, OctopusNavigationMode? navigationMode)
        {
            Mock.Record("OpenCreatePost", prefilled?.Text ?? "", prefilled?.TopicId ?? "", navigationMode);
            if (Mock.Enabled) { Mock.LastPrefilledPost = prefilled; Mock.CurrentScreen = "Create post"; }
        }

        internal static async void ConnectUser(string userId, string nickname, string bio, string picture)
        {
            Mock.Record("ConnectUser", userId, nickname);
            // Mirror the real flow: request a token from the host, then complete.
            if (TokenProvider != null)
            {
                try { await TokenProvider.Invoke(); }
                catch (Exception e) { UnityEngine.Debug.LogError($"[Octopus Mock] tokenProvider threw: {e}"); }
            }
            if (Mock.Enabled) Mock.EmitProfileChanged(new OctopusProfile(clientUserId: userId));
            TriggerOnConnectUserCompleted();
        }

        internal static void DisconnectUser()
        {
            Mock.Record("DisconnectUser");
            if (Mock.Enabled) Mock.EmitProfileChanged(null);
            TriggerOnDisconnectUserCompleted();
        }

        internal static void UpdateNotSeenNotificationsCount()
        {
            Mock.Record("UpdateNotSeenNotificationsCount");
            var settings = OctopusMockSettings.Instance;
            TriggerOnNotSeenNotificationsCount(settings != null ? settings.InitialNotSeenCount : 0);
        }

        internal static void RegisterNotificationsToken(string token) =>
            Mock.Record("RegisterNotificationsToken", token ?? "");

        internal static void Track(string name, IDictionary<string, string> properties)
        {
            Mock.Record("Track", name, properties == null ? 0 : properties.Count);
            if (Mock.Enabled) Mock.LastTrackedEvent = name;
        }

        internal static void TrackAccessToCommunity(bool hasAccess) =>
            Mock.Record("TrackAccessToCommunity", hasAccess);

        internal static void OverrideCommunityAccess(bool hasAccess, Action onCompleted)
        {
            Mock.Record("OverrideCommunityAccess", hasAccess);
            // On device, overriding access changes the reactive value; mirror that so the
            // in-Editor HasAccessToCommunity property and OnHasAccessToCommunityChanged behave faithfully.
            TriggerOnHasAccessToCommunity(hasAccess);
            onCompleted?.Invoke();
        }

        internal static void OverrideDefaultLocale(string languageCode) =>
            Mock.Record("OverrideDefaultLocale", languageCode ?? "");

        internal static void SetUrlInterceptionEnabled(bool enabled) =>
            Mock.Record("SetUrlInterceptionEnabled", enabled);

        internal static void OpenUrlInOctopus(string url) =>
            Mock.Record("OpenUrlInOctopus", url ?? "");

        // Theme setters — recorded as pass-throughs (no overlay effect).
        // Note: the color-scheme setters record unconditionally; on device a null
        // colorScheme is a no-op, so the mock over-records that (null-arg) case.
        internal static void SetLightColorScheme(OctopusColorScheme colorScheme) =>
            Mock.Record("SetLightColorScheme", PackedChannels(colorScheme));
        internal static void SetDarkColorScheme(OctopusColorScheme colorScheme) =>
            Mock.Record("SetDarkColorScheme", PackedChannels(colorScheme));

        /// <summary>
        /// The six packed RGBA ints that would cross the bridge, in bridge argument order
        /// (primary, primaryLow, primaryHigh, onPrimary, link, background). A slot the host left
        /// unset is 0, which both natives read as "keep your default". Empty for a null scheme,
        /// the case a device build turns into a no-op.
        /// </summary>
        private static object[] PackedChannels(OctopusColorScheme colorScheme)
        {
            if (colorScheme == null) return new object[0];
            return new object[]
            {
                ColorToInt(colorScheme.Primary),
                ColorToInt(colorScheme.PrimaryLow),
                ColorToInt(colorScheme.PrimaryHigh),
                ColorToInt(colorScheme.OnPrimary),
                ColorToInt(colorScheme.Link),
                ColorToInt(colorScheme.Background)
            };
        }
        internal static void SetLogo() => Mock.Record("SetLogo");
        internal static void SetAppName(string appName) => Mock.Record("SetAppName", appName ?? "");
        internal static void SetNavBarUsesPrimaryColor(bool usesPrimary) => Mock.Record("SetNavBarUsesPrimaryColor", usesPrimary);
        internal static void SetColorSchemeType(int colorSchemeType) => Mock.Record("SetColorSchemeType", colorSchemeType);
        internal static void SetForcedOrientation(int forcedOrientation) => Mock.Record("SetForcedOrientation", forcedOrientation);
        internal static void SetFonts(OctopusFonts fonts) => Mock.Record("SetFonts", fonts);

        internal static void SyncFollowGroups(
            IList<OctopusSyncFollowGroupAction> actions,
            Action<IList<OctopusSyncFollowGroupResult>> onCompleted)
        {
            Mock.Record("SyncFollowGroups", actions == null ? 0 : actions.Count);
            var results = new List<OctopusSyncFollowGroupResult>();
            if (actions != null)
                foreach (var a in actions)
                    results.Add(new OctopusSyncFollowGroupResult { GroupId = a.GroupId });
            onCompleted?.Invoke(results);
            TriggerOnGroupsChanged(SeedGroups());
        }

        internal static void FetchGroups(Action<IList<OctopusGroup>> onCompleted)
        {
            Mock.Record("FetchGroups");
            onCompleted?.Invoke(SeedGroups());
        }

        static IList<OctopusGroup> SeedGroups() =>
            _groupFollowingGroups != null ? new List<OctopusGroup>(_groupFollowingGroups) :
            OctopusMockSettings.Instance == null
                ? new List<OctopusGroup>()
                : new List<OctopusGroup>(OctopusMockSettings.Instance.SeedGroups);
    }
}
#endif
