using FishNet.Object;
using FishNet.Object.Synchronizing;
using UnityEngine;
using IStalk.Data;
using IStalk.Eye;
using IStalk.Shared;

namespace IStalk.Runner
{
    public class ShrineItemEffect : NetworkBehaviour
    {
        private readonly SyncVar<string> _activeItemId = new();
        private readonly SyncVar<float> _cooldownRemaining = new();

        private ShrineItemData_SO _activeData;
        private float _effectTimer;
        private bool _effectActive;

        public string ActiveItemId => _activeItemId.Value;
        public float CooldownRemaining => _cooldownRemaining.Value;
        public bool HasActiveItem => !string.IsNullOrEmpty(_activeItemId.Value);

        public override void OnStartClient()
        {
            base.OnStartClient();
            _activeItemId.OnChange += OnActiveItemChanged;
        }

        [Server]
        public void ApplyEffect(ShrineItemData_SO data)
        {
            _activeItemId.Value = data.itemId;
            _activeData = data;
            _cooldownRemaining.Value = 0;
        }

        private void Update()
        {
            if (!IsServerStarted) return;

            if (_cooldownRemaining.Value > 0)
                _cooldownRemaining.Value -= Time.deltaTime;

            if (_effectActive)
            {
                _effectTimer -= Time.deltaTime;
                if (_effectTimer <= 0)
                    EndEffect();
            }
        }

        [Server]
        public void ActivateItem()
        {
            if (_activeData == null) return;
            if (_cooldownRemaining.Value > 0) return;

            _effectActive = true;
            _effectTimer = _activeData.effectDuration;
            _cooldownRemaining.Value = _activeData.cooldown;

            switch (_activeData.itemType)
            {
                case ShrineItemType.SmokeBomb:
                    ActivateSmokeBomb();
                    break;
                case ShrineItemType.Mirror:
                    ActivateMirror();
                    break;
                case ShrineItemType.Decoy:
                    ActivateDecoy();
                    break;
                case ShrineItemType.OcularPaint:
                    ActivateOcularPaint();
                    break;
                case ShrineItemType.WeightReducer:
                    ActivateWeightReducer();
                    break;
                case ShrineItemType.GrapplingHook:
                    ActivateGrapplingHook();
                    break;
                case ShrineItemType.FlashOrb:
                    ActivateFlashOrb();
                    break;
                case ShrineItemType.LinkedSprintTotem:
                    ActivateLinkedSprint();
                    break;
                case ShrineItemType.SilenceShroud:
                    ActivateSilenceShroud();
                    break;
                case ShrineItemType.VitalityLink:
                    ActivateVitalityLink();
                    break;
            }

            SpawnVfxObservers();

            if (_activeData.isConsumedOnUse)
            {
                _activeItemId.Value = "";
                _activeData = null;
            }
        }

        [Server]
        private void ActivateSmokeBomb()
        {
            var noise = GetComponent<NoiseEmitter>();
            if (noise != null)
                noise.EmitNoise(transform.position, _activeData.effectRadius, 0);
        }

        [Server]
        private void ActivateMirror()
        {
            var los = LoSManager.Instance;
            if (los != null)
                los.SetRunnerInvisible(ObjectId, _activeData.effectDuration);
        }

        [Server]
        private void ActivateDecoy()
        {
            var los = LoSManager.Instance;
            if (los != null)
            {
                Vector3 decoyPos = transform.position + Random.insideUnitSphere * 8f;
                decoyPos.y = transform.position.y;
                los.AddDecoyBlip(decoyPos, _activeData.effectDuration);
            }
        }

        [Server]
        private void ActivateOcularPaint()
        {
            var los = LoSManager.Instance;
            if (los != null)
                los.BlindEyeArea(transform.position, _activeData.effectRadius, _activeData.effectDuration);
        }

        [Server]
        private void ActivateWeightReducer()
        {
            var weight = GetComponent<WeightComponent>();
            if (weight != null)
                weight.RemoveWeight(_activeData.effectIntensity);
        }

        [Server]
        private void ActivateGrapplingHook()
        {
            var controller = GetComponent<RunnerController>();
            if (controller != null)
                controller.GrantExtraDash();
        }

        [Server]
        private void ActivateFlashOrb()
        {
            var eyeFlash = FindAnyObjectByType<EyeFlashReceiver>();
            if (eyeFlash != null)
                eyeFlash.TryApplyFlash();
        }

        [Server]
        private void ActivateLinkedSprint()
        {
            var buffs = GetComponent<RunnerBuffSystem>();
            if (buffs != null)
                buffs.AddBuff("linked_sprint", _activeData.effectDuration);
        }

        [Server]
        private void ActivateSilenceShroud()
        {
            var noise = GetComponent<NoiseEmitter>();
            if (noise != null)
                noise.Suppress(_activeData.effectDuration);
        }

        [Server]
        private void ActivateVitalityLink()
        {
            var buffs = GetComponent<RunnerBuffSystem>();
            if (buffs != null)
                buffs.AddBuff("vitality_link", _activeData.effectDuration);
        }

        [Server]
        private void EndEffect()
        {
            _effectActive = false;
        }

        [ObserversRpc]
        private void SpawnVfxObservers()
        {
            if (_activeData == null) return;
            if (_activeData.activationVfxPrefab != null)
                Instantiate(_activeData.activationVfxPrefab, transform.position, Quaternion.identity);
            if (_activeData.activationSound != null)
                AudioSource.PlayClipAtPoint(_activeData.activationSound, transform.position);
        }

        private void OnActiveItemChanged(string prev, string next, bool asServer)
        {
            if (string.IsNullOrEmpty(next))
                _activeData = null;
            else
                _activeData = ItemDatabase.Instance.GetShrineItem(next);
        }
    }
}
