// Copyright (c) 2026 EzAdmin. All rights reserved.
// Licensed under the EzAdmin Client License. See LICENSE-CLIENT.txt.

using System.Collections;
using UnityEngine;
using UnityEngine.Events;

namespace EzAdmin.Client
{
    [AddComponentMenu("EzAdmin/EzAdmin Login Gate")]
    public sealed class EzAdminLoginGate : MonoBehaviour
    {
        public enum Requirement
        {
            LinkedToDiscord,
            ConnectedToEzAdmin,
            Staff,
            StaffTools,
        }

        [Tooltip("What the player must be before these switch on.\n" +
                 "Linked To Discord: they linked their Discord account.\n" +
                 "Connected To EzAdmin: signed in and connected, linked or not.\n" +
                 "Staff: their linked Discord holds a moderation permission.\n" +
                 "Staff Tools: they may use in-game staff tools.")]
        [SerializeField] private Requirement _requirement = Requirement.LinkedToDiscord;

        [Tooltip("Switched ON once the requirement is met, OFF until then.")]
        [SerializeField] private GameObject[] _enableWhenReady = new GameObject[0];

        [Tooltip("Switched OFF once the requirement is met, e.g. a \"Link your Discord to play\" sign.")]
        [SerializeField] private GameObject[] _disableWhenReady = new GameObject[0];

        [Tooltip("Scripts or colliders switched on once the requirement is met, e.g. a door's script.")]
        [SerializeField] private Behaviour[] _componentsWhenReady = new Behaviour[0];

        [Tooltip("Switch everything back off if the player stops meeting the requirement (unlinked, lost staff).")]
        [SerializeField] private bool _relockIfLost = true;

        [SerializeField] private UnityEvent _onReady = new UnityEvent();
        [SerializeField] private UnityEvent _onNotReady = new UnityEvent();

        private int _state = -1;

        public bool IsReady => _state == 1;

        public Requirement Needs
        {
            get => _requirement;
            set { _requirement = value; Refresh(); }
        }

        private void Awake()
        {
            Apply(false, true);
        }

        private void OnEnable()
        {
            EzAdminLink.Linked += HandleAny;
            EzAdminLink.StatusChanged += HandleAny;
            EzAdminClient.ConnectionChanged += HandleAny;
            EzAdminClient.SignedIn += HandleAny;
            EzAdminClient.StaffChanged += HandleBool;
            EzAdminClient.StaffToolsChanged += HandleBool;
            StartCoroutine(Watch());
        }

        private void OnDisable()
        {
            EzAdminLink.Linked -= HandleAny;
            EzAdminLink.StatusChanged -= HandleAny;
            EzAdminClient.ConnectionChanged -= HandleAny;
            EzAdminClient.SignedIn -= HandleAny;
            EzAdminClient.StaffChanged -= HandleBool;
            EzAdminClient.StaffToolsChanged -= HandleBool;
        }

        private void HandleAny(string _) => Refresh();

        private void HandleBool(bool _) => Refresh();

        private IEnumerator Watch()
        {
            var wait = new WaitForSecondsRealtime(1f);
            while (true)
            {
                Refresh();
                yield return wait;
            }
        }

        private bool Met()
        {
            var client = EzAdminClient.Instance;
            switch (_requirement)
            {
                case Requirement.LinkedToDiscord: return EzAdminLink.IsLinked;
                case Requirement.ConnectedToEzAdmin: return client != null && client.IsConnected;
                case Requirement.Staff: return EzAdminClient.IsStaff;
                case Requirement.StaffTools: return EzAdminClient.CanUseStaffTools;
                default: return false;
            }
        }

        public void Refresh()
        {
            if (this == null) return;
            bool ready = Met();
            if (!ready && _state == 1 && !_relockIfLost) return;
            int next = ready ? 1 : 0;
            if (next == _state) return;
            Apply(ready, false);
        }

        private void Apply(bool ready, bool silent)
        {
            _state = ready ? 1 : 0;
            foreach (var go in _enableWhenReady) if (go != null) go.SetActive(ready);
            foreach (var go in _disableWhenReady) if (go != null) go.SetActive(!ready);
            foreach (var part in _componentsWhenReady) if (part != null) part.enabled = ready;
            if (silent) return;
            if (ready) _onReady.Invoke(); else _onNotReady.Invoke();
        }
    }
}
