using System;
using System.Collections.Generic;
using System.Linq;
using _PROJECT.Scripts.Bow;
using FishNet.Object;
using FishNet.Object.Synchronizing;
using TMPro;
using Unity.XR.CoreUtils;
using UnityEngine;
using Random = UnityEngine.Random;

public class ArcheryRange : NetworkBehaviour
{
    public TMP_Text highScoreText;
    public TMP_Text timeLeftText;
    public TMP_Text scoreText;

    public Transform targetStartPosition;
    public Transform targetEndPosition;
    public GameObject targetPrefab;
    public StartTarget startTarget;

    public Vector3 minRandomOffset = new Vector3(0f, -2f, -5f);
    public Vector3 maxRandomOffset = new Vector3(0f, 2f, 5f);
    public float roundLength = 60f;
    public float targetSpawnTime = 3f;

    public KeyboardManager keyboardManager;

    private List<ArcheryTarget> _targets;
    
    [SyncVar]
    private float _score;
    private float _maxScore;
    private float _roundEndTime;
    private float _nextTargetTime;
    private bool _roundActive;

    private readonly List<XROrigin> _presentPlayers = new();

    private XROrigin _scoredPlayer;

    public override void OnStartServer()
    {
        base.OnStartServer();
        _roundActive = false;
        _targets = new List<ArcheryTarget>();
        _score = 0;
        _maxScore = 0;
        _roundEndTime = 0f;
        _maxScore = LoadScoreFromDisk();
        //+Debug.Log($"highScore is {_maxScore}");

        StartCoroutine(DelayedSetHighScoreText(10f)); // Cannot immediately assign these values because fishnet needs time.
    }

    private System.Collections.IEnumerator DelayedSetHighScoreText(float delaySeconds)
    {
        yield return new WaitForSeconds(delaySeconds);
        SetHighScoreText($"High Score: {_maxScore}");
    }

    private void OnTriggerEnter(Collider other)
    {
        SetHighScoreText($"High Score: {_maxScore}"); // Refresh high score UI

        XROrigin enteredPlayer = other.GetComponent<XROrigin>();
        if (enteredPlayer == null || _presentPlayers.Contains(enteredPlayer))
            return;

        _presentPlayers.Add(enteredPlayer); // Adds to the end
    }

    private void OnTriggerExit(Collider other)
    {
        XROrigin exitedPlayer = other.GetComponent<XROrigin>();
        if (exitedPlayer == null) return;

        _presentPlayers.Remove(exitedPlayer); // Shifts others left automatically

        if (exitedPlayer == _scoredPlayer) keyboardManager.DeActivate(); // If the player �refuses to enter their name.

    }



    public override void OnStopServer()
    {
        base.OnStopServer();
        SaveScoreToDisk(_maxScore);
    }

    // Update is called once per frame
    void Update()
    {
        if (!IsServer) return;

        if (!_roundActive) return;

        if (Time.time >= _roundEndTime)
        {
            ResetRange();
        }
        else
        {
            SetTimeLeftText($"Time Left: {Math.Ceiling((_roundEndTime - Time.time) % 60)}");
            if (Time.time >= _nextTargetTime)
            {
                _nextTargetTime = Time.time + targetSpawnTime;
                SpawnTarget();
            }
        }
    }

    private void SpawnTarget()
    {
        if (!IsServer) return;
        
        var randomPos = targetStartPosition.position + new Vector3(
            Random.Range(minRandomOffset.x, maxRandomOffset.x),
            (float)Math.Round(Random.Range(minRandomOffset.y, maxRandomOffset.y)),
            Random.Range(minRandomOffset.z, maxRandomOffset.z));

        var target = SpawnTarget(randomPos);

        _targets.Add(target);
    }

    private ArcheryTarget SpawnTarget(Vector3 randomPos)
    {
        var prefab = Instantiate(targetPrefab, randomPos, Quaternion.identity, null);
        ArcheryTarget target = prefab.GetComponent<ArcheryTarget>();
        target.endPosition = targetEndPosition.position;
        target.addScore = AddScore;
        Spawn(prefab);
        return target;
    }


    public void ResetRange()
    {
        if (!IsServer) return;
        foreach (var target in _targets.Where(target => target != null))
        {
            Despawn(target.gameObject, DespawnType.Pool);
        }
        
        _targets = new List<ArcheryTarget>();
        if (_maxScore < _score) _maxScore = _score;

        if(_presentPlayers.Count != 0) // If there are players in the area.
        {
            // Gives the score to the player longest-lasting in the area. It would be better to give it to the player that fired the starting arrow, but I'm not spending 10 hours on this. 

            XROrigin scoringPlayer = _presentPlayers.First(); 

            Menu playermenu = scoringPlayer.GetComponentInChildren<Menu>();

            _scoredPlayer = scoringPlayer;

            playermenu.setCanvasVisibility(false); // Deactivates the menu to avoid overlap issues.

            Transform keyboardPlacement = playermenu.updateMenuTransform();

            keyboardManager.Activate(_score, keyboardPlacement);

            SaveScoreToDisk(_maxScore);
            SetHighScoreText($"High Score: {_maxScore}");
        } 
        _score = 0;
        
        startTarget.ShowTarget();
        
        _roundActive = false;
        SetTimeLeftText("");
    }

    public void StartRound()
    {
        if (!IsServer) return;
        _roundEndTime = Time.time + roundLength;
        _nextTargetTime = Time.time;
        _roundActive = true;
    }

    public void AddScore(float distance)
    {
        if (!IsServer) return;
        _score += distance;
        SetScoreText($"Score: {_score}");
    }

    [ObserversRpc]
    public void SetHighScoreText(string text)
    {
        highScoreText.text = text;
    }
    
    [ObserversRpc]
    public void SetScoreText(string text)
    {
        scoreText.text = text;
    }
    
    [ObserversRpc]
    public void SetTimeLeftText(string text)
    {
        timeLeftText.text = text;
    }
    public class SaveData
    {
        public float HighScore;
    }

    public void SaveScoreToDisk(float highScore)
    {
        SaveData data = new SaveData { HighScore = highScore };
        string json = JsonUtility.ToJson(data);
        System.IO.File.WriteAllText("highscore.json", json);
    }
    public float LoadScoreFromDisk()
    {
        if (System.IO.File.Exists("highscore.json"))
        {
            string json = System.IO.File.ReadAllText("highscore.json");
            SaveData data = JsonUtility.FromJson<SaveData>(json);
            return data.HighScore;
        }

        return -1f; // default score
    }

}