using System.Collections;
using System.Collections.Generic;
using Core.Domain;
using Core.Ports;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Infrastructure.Unity
{
    public class GameBootstrap : MonoBehaviour
    {
        [Header("Infrastructure")]
        [SerializeField] private LevelGenerator levelGenerator;
        [SerializeField] private OrbViewAdapter orbPrefab;
        [SerializeField] private PlayerController playerPrefab;
        [SerializeField] private DeathPlaneAdapter deathPlanePrefab;
        [SerializeField] private SoundManager soundManager;
        [SerializeField] private RumbleManager rumbleManager;
        [SerializeField] private BeatPulseController beatPulseController;
        [SerializeField] private CameraController cameraController;
        [SerializeField] private NpcController npcPrefab;
        [SerializeField] private HunterNpcController hunterNpcPrefab;
        [SerializeField] private FloorVisibilityManager floorVisibilityManager;

        [Header("Ui")]
        [SerializeField] private TMP_Text scoreText;
        [SerializeField] private TMP_Text highScoreText;
        [SerializeField] private GameObject gameOverUi;
        [SerializeField] private GameObject pauseUi;
        [SerializeField] private GameObject startScreenUi;

        [Header("Settings")]
        [SerializeField] private float restartTime = 3f;

        [Header("Power Ups")]
        [SerializeField] private PowerUpViewAdapter powerUpPrefab;

        private readonly List<Tile> _allTiles = new();
        private readonly Dictionary<string, TileViewAdapter> _tileViews = new();
        private GameSession _gameSession;
        private IPersistenceService _persistenceService;
        private InputSystem_Actions _actions;
        private PlayerController _playerInstance;
        private GameObject _currentOrbInstance;
        private bool _isGameRunning;
        private int _currentPlayerFloorIndex;
        private int _currentDisplayedScore;
        private float _inputBlockTimer;
        private bool _isPaused;
        private bool _levelGenerated;

        private void OnEnable()
        {
            _actions = new InputSystem_Actions();
            _actions.Player.Enable();
        }

        private void OnDisable()
        {
            _actions.Player.Disable();
            beatPulseController.OnMeasure.RemoveListener(OnBeatMeasure);
        }


        private void Start()
        {
            _inputBlockTimer = 0.5f;
            _persistenceService = new PlayerPrefsPersistenceAdapter();
            _gameSession = new GameSession(_allTiles, _persistenceService);

            // Set Theme based on High Score
            ThemeManager.CurrentTheme = ThemeManager.GetTheme(_gameSession.HighScore);
            
            var floorsCount = levelGenerator ? levelGenerator.FloorsCount : 1;
            
            if (levelGenerator)
            {
                StartCoroutine(levelGenerator.GenerateAsync(soundManager, _allTiles, _tileViews, cameraController,
                    rumbleManager,
                    () =>
                    {
                        if (!floorVisibilityManager)
                        {
                            floorVisibilityManager = gameObject.AddComponent<FloorVisibilityManager>();
                        }
                        floorVisibilityManager.Initialize(_gameSession, _allTiles, _tileViews, floorsCount);

                        SpawnDeathPlane();
                        SpawnPlayer();

                        if (gameOverUi) gameOverUi.SetActive(false);
                        if (startScreenUi) startScreenUi.SetActive(true); // Show start screen NOW

                        WireEvents();
                        UpdateScoreUi(_gameSession.Score);
                        
                        _levelGenerated = true;
                    }));
            }

            if (gameOverUi) gameOverUi.SetActive(false);
            if (startScreenUi) startScreenUi.SetActive(true);
        }

        private void Update()
        {
            if (_inputBlockTimer > 0f)
            {
                _inputBlockTimer -= Time.deltaTime;
                return;
            }

            if (_actions.Player.Pause.triggered && _isGameRunning)
            {
                TogglePause();
            }

            if (_isPaused) return;
            
            if (!_isGameRunning && _levelGenerated)
            {
                if (_actions.Player.StartGame.triggered)
                {
                    StartGameSequence();
                }

                return;
            }

            if (_playerInstance)
            {
                var playerY = _playerInstance.transform.position.y;
                // Calculate current floor index based on Y height (inverse logic from Generator)
                // Note: Generator uses negative offsets: 0, -15, -30.
                // So Floor 0 is at Y=0. Floor 1 is at Y=-15.
                
                var heightDist = levelGenerator.FloorHeightDistance;
                var maxFloors = levelGenerator.FloorsCount;
    
                var rawFloor = Mathf.RoundToInt(-playerY / heightDist);
                _currentPlayerFloorIndex = Mathf.Clamp(rawFloor, 0, maxFloors - 1);

                _gameSession.SetPlayerFloor(_currentPlayerFloorIndex);
    
                floorVisibilityManager.UpdateFloorVisibility(_currentPlayerFloorIndex);
            }

            var dt = Time.deltaTime;

            if (_isGameRunning) _gameSession.Tick(dt);

            var dilation = _gameSession.TimeDilation;

            // Hard Mode: Decay faster as score increases
            var decayMultiplier = 1.0f + (_gameSession.Score / 500f);

            for (var i = _allTiles.Count - 1; i >= 0; i--)
            {
                _allTiles[i].Tick(dt * dilation * decayMultiplier);
            }
        }

        private void TogglePause()
        {
            _isPaused = !_isPaused;

            if (_isPaused)
            {
                Time.timeScale = 0f;
                if (soundManager) soundManager.SetPaused(true);
                if (pauseUi) pauseUi.SetActive(true);
                if (rumbleManager) rumbleManager.SetPaused(true);
            }
            else
            {
                Time.timeScale = 1f;
                if (soundManager) soundManager.SetPaused(false);
                if (pauseUi) pauseUi.SetActive(false);
                if (rumbleManager) rumbleManager.SetPaused(false);
            }
        }

        private void SpawnVisualOrb(string tileId)
        {
            if (_currentOrbInstance) Destroy(_currentOrbInstance);

            if (!_tileViews.TryGetValue(tileId, out var tileView)) return;
            if (!tileView) return;

            var spawnPos = tileView.transform.position + Vector3.up;
            var orb = Instantiate(orbPrefab, spawnPos, Quaternion.identity);

            orb.OnCollected += () => _gameSession.OrbCollected();

            _currentOrbInstance = orb.gameObject;
        }

        private void HandleOrbReset()
        {
            if (_currentOrbInstance)
            {
                Destroy(_currentOrbInstance);
            }
        }

        private void UpdateScoreUi(int newScore)
        {
            if (!scoreText) return;
            
            LeanTween.cancel(scoreText.gameObject);
            
            scoreText.rectTransform.localScale = Vector3.one; 
            LeanTween.scale(scoreText.rectTransform, Vector3.one * 1.5f, 0.5f)
                .setEasePunch();

            LeanTween.value(scoreText.gameObject, (float val) => 
                {
                    var currentVal = Mathf.RoundToInt(val);
        
                    var combo = _gameSession?.ComboMultiplier ?? 1;
                    var comboText = combo > 1 ? $" <color=yellow>x{combo}</color>" : ""; 
        
                    scoreText.text = $"{currentVal}{comboText}";
                }, _currentDisplayedScore, newScore, 0.5f)
                .setEaseOutExpo();

            _currentDisplayedScore = newScore; 

            if (highScoreText && _gameSession != null) 
                highScoreText.text = $"BEST: {_gameSession.HighScore}";

        }

        private void SpawnPlayer()
        {
            var spawnPos = new Vector3(0f, 5f, 0f);
            _playerInstance = Instantiate(playerPrefab, spawnPos, Quaternion.identity);

            _playerInstance.enabled = false;

            _playerInstance.Rigidbody.isKinematic = true;

            if (cameraController)
            {
                cameraController.SetTarget(_playerInstance.transform);
            }
        }

        private void SpawnDeathPlane()
        {
            if (!levelGenerator) return;

            var lowestY = -(levelGenerator.FloorsCount * levelGenerator.FloorHeightDistance) - 5f;
            var pos = new Vector3(levelGenerator.GridSizeX / 2f, lowestY, levelGenerator.GridSizeY / 2f);
            var plane = Instantiate(deathPlanePrefab, pos, Quaternion.identity);
            plane.transform.localScale = new Vector3(levelGenerator.GridSizeX * 200f, 1f, levelGenerator.GridSizeY * 200f);

            plane.OnPlayerFell += () => _gameSession.EndGame();
        }

        private void HandleGameOver()
        {
            _isGameRunning = false;
            
            if (beatPulseController) beatPulseController.StopTracking();
            
            if (gameOverUi) gameOverUi.SetActive(true);

            StartCoroutine(RestartRoutine());
        }

        private IEnumerator RestartRoutine()
        {
            yield return new WaitForSeconds(restartTime);

            SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
        }

        private void WireEvents()
        {
            _gameSession.OnScoreChanged += UpdateScoreUi;
            _gameSession.OnOrbSpawned += SpawnVisualOrb;
            _gameSession.OnOrbReset += HandleOrbReset;
            _gameSession.OnGameOver += HandleGameOver;
            _gameSession.OnSpawnNpc += SpawnNpc;
            _gameSession.OnSpawnPowerUp += SpawnPowerUp;

            if (beatPulseController)
            {
                beatPulseController.OnMeasure.AddListener(OnBeatMeasure);
            }

            if (!soundManager) return;

            _gameSession.OnScoreChanged += _ => soundManager.PlayScore();
            _gameSession.OnGameOver += () =>
            {
                soundManager.StopMusic();
                soundManager.PlayGameOver();
            };
        }

        private void SpawnNpc()
        {
            var validTiles = _allTiles.FindAll(t => t.Floor == 0 && t.CurrentState == TileState.Stable);

            if (validTiles.Count == 0)
            {
                validTiles = _allTiles.FindAll(t => t.CurrentState == TileState.Stable);
            }

            if (validTiles.Count == 0) return;

            var randomTile = validTiles[Random.Range(0, validTiles.Count)];

            if (!_tileViews.TryGetValue(randomTile.Id, out var tileView)) return;
            if (!tileView) return;

            var spawnPos = tileView.transform.position + Vector3.up * 5f;
            
            if (_playerInstance && hunterNpcPrefab && Random.value < 0.3f)
            {
                var hunter = Instantiate(hunterNpcPrefab, spawnPos, Quaternion.identity);
                hunter.Initialize(_playerInstance, () => _gameSession.EndGame(), () => _gameSession.TimeDilation);
            }
            else if (npcPrefab)
            {
                var npc = Instantiate(npcPrefab, spawnPos, Quaternion.identity);
                npc.Initialize(() => _gameSession.TimeDilation, () => _gameSession.EndGame());
            }

            soundManager.PlayNpcSpawn();
        }

        private void StartGameSequence()
        {
            _isGameRunning = true;
            _currentDisplayedScore = 0;
            
            if (startScreenUi) startScreenUi.SetActive(false);

            if (soundManager)
            {
                soundManager.PlayGameStart();
                soundManager.PlayMusic();
                
                if (beatPulseController) beatPulseController.BeginTracking();
            }

            if (_playerInstance)
            {
                _playerInstance.enabled = true;
                _playerInstance.Rigidbody.isKinematic = false;
            }

            _gameSession.StartGame();
        }

        private void SpawnPowerUp(PowerUpType type, string tileId)
        {
            if (!_tileViews.TryGetValue(tileId, out var tileView)) return;
            if (!tileView) return;

            var spawnPos = tileView.transform.position + Vector3.up * 0.5f;
            var instance = Instantiate(powerUpPrefab, spawnPos, Quaternion.identity);
            
            instance.Configure(type);

            instance.OnCollected += (t) =>
            {
                cameraController?.Shake(0.2f, 0.15f);
                rumbleManager?.PulseMedium();
                
                if (t == PowerUpType.TimeSlow)
                {
                    _gameSession.ActivateTimeSlow(10f);
                }
            };
        }

        private void OnBeatMeasure()
        {
            if (_allTiles.Count == 0) return;

            var pulseCount = 25;
            
            for (var i = 0; i < pulseCount; i++)
            {
                var randIndex = Random.Range(0, _allTiles.Count);
                var tile = _allTiles[randIndex];
                
                if (tile.Floor < _currentPlayerFloorIndex) continue;
                if (tile.Floor > _currentPlayerFloorIndex + 1) continue;

                if (tile.CurrentState != TileState.Stable) continue;
        
                if (_tileViews.TryGetValue(tile.Id, out var tileView))
                {
                    tileView.PulseEmission(Random.Range(1.2f, 2.0f));
                }
            }
        }
    }
}