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 CameraController cameraController;
        [SerializeField] private NpcController npcPrefab;
        [SerializeField] private HunterNpcController hunterNpcPrefab;

        [Header("Level Generation")]
        [SerializeField] private int floorsCount = 3;
        [SerializeField] private float floorHeightDistance = 15f;

        [Header("Ui")]
        [SerializeField] private TMP_Text scoreText;
        [SerializeField] private TMP_Text highScoreText;
        [SerializeField] private GameObject gameOverUi;
        [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 void OnEnable()
        {
            _actions = new InputSystem_Actions();
            _actions.Player.Enable();
        }

        private void OnDisable()
        {
            _actions.Player.Disable();
        }


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

            // Set Theme based on High Score
            ThemeManager.CurrentTheme = ThemeManager.GetTheme(_gameSession.HighScore);

            if (levelGenerator) levelGenerator.Generate(soundManager, _allTiles, _tileViews, cameraController);

            SpawnDeathPlane();
            SpawnPlayer();

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

            WireEvents();
            UpdateScoreUi(_gameSession.Score);
        }

        private void Update()
        {
            if (!_isGameRunning)
            {
                if (_actions.Player.StartGame.triggered)
                {
                    StartGameSequence();
                }

                return;
            }

            if (_playerInstance)
            {
                var playerY = _playerInstance.transform.position.y;
                var currentFloor = Mathf.RoundToInt(-playerY / floorHeightDistance);

                currentFloor = Mathf.Clamp(currentFloor, 0, floorsCount - 1);

                _gameSession.SetPlayerFloor(currentFloor);
            }

            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 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;

            var combo = _gameSession?.ComboMultiplier ?? 1;
            var comboText = combo > 1 ? $" (x{combo})" : "";

            scoreText.text = $"Data: {newScore}{comboText}";

            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 * 2f, 1f, levelGenerator.GridSizeY * 2f);

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

        private void HandleGameOver()
        {
            _isGameRunning = false;
            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 (!soundManager) return;

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

        private void SpawnNpc()
        {
            var spawnPos = new Vector3(levelGenerator.GridSizeX / 2f, 7f, levelGenerator.GridSizeY / 2f);

            // 30% chance for Hunter if player available
            if (_playerInstance && hunterNpcPrefab && Random.value < 0.3f)
            {
                var hunter = Instantiate(hunterNpcPrefab, spawnPos, Quaternion.identity);
                hunter.Initialize(_playerInstance);
            }
            else if (npcPrefab)
            {
                Instantiate(npcPrefab, spawnPos, Quaternion.identity);
            }

            soundManager.PlayNpcSpawn();
        }

        private void StartGameSequence()
        {
            _isGameRunning = true;

            if (startScreenUi) startScreenUi.SetActive(false);

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

            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) =>
            {
                if (t == PowerUpType.TimeSlow)
                {
                    _gameSession.ActivateTimeSlow(10f);
                }
            };
        }
    }
}