using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Magisterka.BulletHell
{
    /// <summary>
    /// Handles user input, shooting, screen-clearing bomb, and respawn logic.
    /// </summary>
    [RequireComponent(typeof(Health))]
    public class PlayerController : MonoBehaviour
    {
        public static PlayerController Instance { get; private set; }

        [Header("Movement")]
        [SerializeField] private float moveSpeed = 12f;
        [SerializeField] private float focusSpeedMultiplier = 0.6f;

        [Header("Offense")]
    [SerializeField] private float fireRate = 0.12f;
    [SerializeField] private float bulletSpeed = 22f;
    [SerializeField] private float bulletDamage = 14f;
    [SerializeField] private int volleyWidth = 5;

    [Header("Survivability")]
    [SerializeField] private int maxLives = 1;
    [SerializeField] private float respawnDelay = 0.7f;
        [SerializeField] private float invulnerabilityDuration = 2f;

    [Header("Progression")]
    [SerializeField] private int baseLevelThreshold = 1500;

        [Header("Controls")]
        [SerializeField] private KeyCode fireKey = KeyCode.Z;
        [SerializeField] private KeyCode modifierKey = KeyCode.LeftShift;
        [SerializeField] private KeyCode bombKey = KeyCode.X;
        [SerializeField] private KeyCode invincibilityToggleKey = KeyCode.I;
        [SerializeField] private KeyCode inactiveModeToggleKey = KeyCode.P;

    private const int MaxLevel = 12;

    private BulletPool _playerBulletPool;
        private Health _health;
        private SpriteRenderer _renderer;
        private Vector2 _bounds = new Vector2(8f, 4.5f);
        private float _fireCooldown;
        private bool _bombAvailable = true;
        private int _lives;
        private bool _canControl = true;
        private bool _isAlive = true;
        private bool _isInvulnerable;
        private bool _victoryAchieved;
        private Color _baseColor;
        private Coroutine _hitFlashRoutine;
    private Coroutine _levelFlashRoutine;
    private int _level;
    private float _baseBulletSpeed;
    private int _baseVolleyWidth;
    
    // Testing modes
    private bool _permanentInvincibility;
    private bool _inactiveMode;

        public void Initialize(BulletPool bulletPool, Vector2 movementBounds)
        {
            Instance = this;
            _playerBulletPool = bulletPool;
            _bounds = movementBounds;
            _health = GetComponent<Health>();
            _health.Configure(Faction.Player, 60f);
            _health.Died += HandleDeath;
            _health.Damaged += HandleDamageTaken;

            _renderer = GetComponent<SpriteRenderer>();
            _baseColor = _renderer != null ? _renderer.color : Color.cyan;

            _baseBulletSpeed = bulletSpeed;
            _baseVolleyWidth = volleyWidth;
            _level = 0;

            _lives = Mathf.Max(0, maxLives);
            ScoreManager.Instance?.SetLives(_lives);
            transform.position = new Vector3(0f, -_bounds.y + 1.5f, 0f);
            _isAlive = true;
            _canControl = true;
            _bombAvailable = true;
            _fireCooldown = 0f;
            _victoryAchieved = false;

            // Check command-line arguments for test modes
            CheckCommandLineArgs();

            if (ScoreManager.Instance != null)
            {
                ScoreManager.Instance.ScoreChanged += HandleScoreChanged;
                HandleScoreChanged(ScoreManager.Instance.CurrentScore);
            }
        }

        /// <summary>
        /// Check command-line arguments for automated test modes.
        /// Usage: ./Final.x86_64 --invincible --stationary
        /// </summary>
        private void CheckCommandLineArgs()
        {
            string[] args = System.Environment.GetCommandLineArgs();
            foreach (string arg in args)
            {
                if (arg == "--invincible" || arg == "-i")
                {
                    _permanentInvincibility = true;
                    _isInvulnerable = true;
                    Debug.Log("[TEST] Command-line: Invincibility ENABLED");
                    if (_renderer != null)
                    {
                        _renderer.color = Color.yellow;
                    }
                }
                else if (arg == "--stationary" || arg == "-s")
                {
                    _inactiveMode = true;
                    Debug.Log("[TEST] Command-line: Stationary mode ENABLED");
                }
            }
        }

        public static PlayerController CreatePlayer(Transform parent, BulletPool playerBulletPool, Vector2 movementBounds)
        {
            var playerGO = new GameObject("Player");
            playerGO.transform.SetParent(parent, false);

            var renderer = playerGO.AddComponent<SpriteRenderer>();
            renderer.sprite = BuildPlayerSprite();
            renderer.sortingOrder = 20;
            renderer.color = new Color(0.25f, 0.85f, 1f, 1f);

            var collider = playerGO.AddComponent<CircleCollider2D>();
            collider.radius = 1f / 64f;
            collider.isTrigger = true;

            playerGO.AddComponent<Health>();

            var controller = playerGO.AddComponent<PlayerController>();
            controller.Initialize(playerBulletPool, movementBounds);
            return controller;
        }

    public int MaxLives => Mathf.Max(1, maxLives);
    public int CurrentLives => Mathf.Max(0, _lives);

        private void Update()
        {
            if (_victoryAchieved)
            {
                return;
            }

            // Toggle invincibility with I key
            if (Input.GetKeyDown(invincibilityToggleKey))
            {
                _permanentInvincibility = !_permanentInvincibility;
                Debug.Log($"[TEST] Invincibility: {(_permanentInvincibility ? "ON" : "OFF")}");
                if (_permanentInvincibility)
                {
                    _isInvulnerable = true;
                    if (_renderer != null)
                    {
                        _renderer.color = Color.yellow;
                    }
                }
                else
                {
                    _isInvulnerable = false;
                    if (_renderer != null)
                    {
                        _renderer.color = _baseColor;
                    }
                }
            }

            // Toggle inactive mode with P key
            if (Input.GetKeyDown(inactiveModeToggleKey))
            {
                _inactiveMode = !_inactiveMode;
                Debug.Log($"[TEST] Inactive Mode: {(_inactiveMode ? "ON (stationary)" : "OFF (active)")}");
            }

            if (_fireCooldown > 0f)
            {
                _fireCooldown -= Time.deltaTime;
            }

            if (!_canControl)
            {
                return;
            }

            // Skip movement and shooting in inactive mode
            if (!_inactiveMode)
            {
                HandleMovement();
                HandleShooting();
            }
            HandleBomb();
        }

        private void HandleMovement()
        {
            if (!_canControl)
            {
                return;
            }

            float x = 0f;
            float y = 0f;

            if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) x -= 1f;
            if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) x += 1f;
            if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) y += 1f;
            if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) y -= 1f;

            var direction = new Vector2(x, y).normalized;
            float speed = moveSpeed * (Input.GetKey(modifierKey) ? focusSpeedMultiplier : 1f);
            var delta = direction * (speed * Time.deltaTime);
            var next = transform.position + (Vector3)delta;
            next.x = Mathf.Clamp(next.x, -_bounds.x + 0.5f, _bounds.x - 0.5f);
            next.y = Mathf.Clamp(next.y, -_bounds.y + 0.5f, _bounds.y - 0.5f);
            transform.position = next;
        }

        private void HandleShooting()
        {
            if (!_canControl || !_isAlive)
            {
                return;
            }

            bool wantsFire = Input.GetKey(fireKey) || Input.GetMouseButton(0);
            if (!wantsFire || _fireCooldown > 0f)
            {
                return;
            }

            FireVolley();
            _fireCooldown = fireRate;
        }

        private void FireVolley()
        {
            var origin = transform.position + Vector3.up * 0.35f;
            float speed = GetLevelAdjustedBulletSpeed();
            int count = Mathf.Clamp(_baseVolleyWidth + Mathf.Max(0, _level), 1, 17);
            float mid = (count - 1) * 0.5f;
            float spreadStep = Mathf.Lerp(5f, 8f, Mathf.Clamp01(_level / 12f));

            for (int i = 0; i < count; i++)
            {
                float offsetIndex = i - mid;
                float spread = offsetIndex * spreadStep;
                var direction = Quaternion.Euler(0f, 0f, spread) * Vector2.up;
                var offset = new Vector3(offsetIndex * 0.18f, 0f, 0f);
                _playerBulletPool.Spawn(origin + offset, direction, speed, bulletDamage);
            }

            foreach (var extra in GetExtraDirectionsForLevel())
            {
                var dir = extra.normalized;
                var spawnPos = transform.position + (Vector3)(dir * 0.4f);
                _playerBulletPool.Spawn(spawnPos, dir, speed, bulletDamage);
            }

            EffectManager.Instance?.SpawnHitEffect(origin + Vector3.up * 0.1f, Faction.Player, 0.4f + _level * 0.05f);
        }

        private void HandleBomb()
        {
            if (!_bombAvailable || !_canControl || !_isAlive)
            {
                return;
            }

            bool trigger = Input.GetKeyDown(bombKey) || Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(1);
            if (!trigger)
            {
                return;
            }

            _bombAvailable = false;
            StartCoroutine(PerformBomb());
        }

        private IEnumerator PerformBomb()
        {
            _canControl = false;
            _isInvulnerable = true;
            _renderer.color = Color.white;
            EffectManager.Instance?.SpawnScreenClear(transform.position, 18f);
            EnemySpawner.Instance?.ClearScreen();

            // Hyper dash upwards and back for dramatic flair.
            Vector3 start = transform.position;
            Vector3 apex = new Vector3(0f, Mathf.Min(_bounds.y - 0.6f, start.y + 4.5f), 0f);
            float travel = 0.4f;
            float elapsed = 0f;

            while (elapsed < travel)
            {
                elapsed += Time.deltaTime;
                float t = elapsed / travel;
                transform.position = Vector3.Lerp(start, apex, t);
                yield return null;
            }

            elapsed = 0f;
            while (elapsed < travel)
            {
                elapsed += Time.deltaTime;
                float t = elapsed / travel;
                transform.position = Vector3.Lerp(apex, start, t);
                yield return null;
            }

            // Only disable invulnerability if permanent mode is off
            if (!_permanentInvincibility)
            {
                _isInvulnerable = false;
                _renderer.color = _baseColor;
            }
            _canControl = true;
        }

        private void HandleDeath(Health _)
        {
            if (!_isAlive)
            {
                return;
            }

            _isAlive = false;
            _canControl = false;
            _bombAvailable = false;
            if (_hitFlashRoutine != null)
            {
                StopCoroutine(_hitFlashRoutine);
                _renderer.color = _baseColor;
                _hitFlashRoutine = null;
            }
            _lives -= 1;
            ScoreManager.Instance?.SetLives(Mathf.Max(0, _lives));
            StartCoroutine(RespawnRoutine());
        }

        private IEnumerator RespawnRoutine()
        {
            EffectManager.Instance?.SpawnExplosion(transform.position, Faction.Player, 4f);
            _renderer.enabled = false;
            _isInvulnerable = true;

            if (_lives < 0)
            {
                yield return new WaitForSeconds(1f);
                ScoreManager.Instance?.ShowGameOver();
                EnemySpawner.Instance?.StopAndClear();
                GameDirector.Instance?.OnPlayerGameOver();
                gameObject.SetActive(false);
                yield break;
            }

            yield return new WaitForSeconds(respawnDelay);
            EnemySpawner.Instance?.ClearScreen();
            transform.position = new Vector3(0f, -_bounds.y + 1.5f, 0f);
            _renderer.enabled = true;
            _health.RestoreFull();
            _fireCooldown = fireRate;
            _canControl = true;

            float timer = invulnerabilityDuration;
            while (timer > 0f)
            {
                timer -= Time.deltaTime;
                float pulse = Mathf.PingPong(Time.time * 10f, 1f);
                if (_renderer != null)
                {
                    _renderer.color = Color.Lerp(_baseColor, Color.white, pulse);
                }

                yield return null;
            }

            if (_renderer != null)
            {
                _renderer.color = _baseColor;
            }

            // Only disable invulnerability if permanent mode is off
            if (!_permanentInvincibility)
            {
                _isInvulnerable = false;
                _renderer.color = _baseColor;
            }
            else
            {
                _renderer.color = Color.yellow;
            }
            _isAlive = true;
            _canControl = true;
        }

        private void OnDestroy()
        {
            if (_health != null)
            {
                _health.Died -= HandleDeath;
                _health.Damaged -= HandleDamageTaken;
            }

            if (ScoreManager.Instance != null)
            {
                ScoreManager.Instance.ScoreChanged -= HandleScoreChanged;
            }

            if (Instance == this)
            {
                Instance = null;
            }
        }

        public bool IsInvulnerable() => _isInvulnerable;

        public void ApplyDamage(float value)
        {
            if (_isInvulnerable || !_isAlive || _victoryAchieved)
            {
                return;
            }

            _health.ApplyDamage(value, transform.position);
        }

        public void HandleVictory()
        {
            _victoryAchieved = true;
            _canControl = false;
            _isInvulnerable = true;
            if (_renderer != null)
            {
                _renderer.color = Color.white;
            }
        }

        private void HandleDamageTaken(float damage, Vector2 hitPoint)
        {
            if (_renderer == null || _isInvulnerable || !_isAlive)
            {
                return;
            }

            if (_hitFlashRoutine != null)
            {
                StopCoroutine(_hitFlashRoutine);
                _renderer.color = _baseColor;
            }

            _hitFlashRoutine = StartCoroutine(HitFlashRoutine());
            float strength = Mathf.Clamp01(damage / Mathf.Max(1f, _health.MaxHealth * 0.4f));
            ScoreManager.Instance?.TriggerDamageFlash(strength);
            EffectManager.Instance?.ShakeCamera(0.22f, 0.65f);
        }

        private IEnumerator HitFlashRoutine()
        {
            const float duration = 0.35f;
            float elapsed = 0f;

            while (elapsed < duration)
            {
                elapsed += Time.deltaTime;
                float t = Mathf.PingPong(elapsed * 18f, 1f);
                _renderer.color = Color.Lerp(_baseColor, Color.white, t);
                yield return null;
            }

            _renderer.color = _baseColor;
            _hitFlashRoutine = null;
        }

        private float GetLevelAdjustedBulletSpeed()
        {
            return _baseBulletSpeed;
        }

        private IEnumerable<Vector2> GetExtraDirectionsForLevel()
        {
            if (_level <= 0)
            {
                yield break;
            }

            if (_level >= 2)
            {
                yield return Vector2.left;
                yield return Vector2.right;
            }

            if (_level >= 4)
            {
                yield return (Vector2.left + Vector2.up).normalized;
                yield return (Vector2.right + Vector2.up).normalized;
            }

            if (_level >= 6)
            {
                yield return Vector2.down;
            }

            if (_level >= 8)
            {
                yield return (Vector2.left + Vector2.down * 0.7f).normalized;
                yield return (Vector2.right + Vector2.down * 0.7f).normalized;
            }
        }

        private void HandleScoreChanged(int score)
        {
            int newLevel = CalculateLevelFromScore(score);

            if (newLevel > _level)
            {
                for (int next = _level + 1; next <= newLevel; next++)
                {
                    _level = next;
                    HandleLevelUpFeedback();
                }
            }
            else if (newLevel < _level)
            {
                _level = newLevel;
            }
        }

        private int CalculateLevelFromScore(int score)
        {
            if (baseLevelThreshold <= 0)
            {
                return 0;
            }

            int level = 0;
            int increment = baseLevelThreshold;
            int threshold = baseLevelThreshold;

            while (level < MaxLevel && score >= threshold)
            {
                level++;
                increment *= 2;
                threshold += increment;
            }

            return level;
        }

        private void HandleLevelUpFeedback()
        {
            EffectManager.Instance?.SpawnExplosion(transform.position, Faction.Player, 2f + _level * 0.2f);
            EffectManager.Instance?.ShakeCamera(0.18f, 0.4f + _level * 0.05f);

            if (_hitFlashRoutine != null)
            {
                StopCoroutine(_hitFlashRoutine);
                _hitFlashRoutine = null;
            }

            if (_renderer != null)
            {
                _renderer.color = _baseColor;
            }

            if (_levelFlashRoutine != null)
            {
                StopCoroutine(_levelFlashRoutine);
            }

            _levelFlashRoutine = StartCoroutine(LevelUpFlashRoutine());
        }

        private IEnumerator LevelUpFlashRoutine()
        {
            float duration = 0.6f;
            float elapsed = 0f;
            var glowColor = Color.Lerp(_baseColor, new Color(1f, 0.95f, 0.35f, 1f), 0.85f);

            while (elapsed < duration)
            {
                elapsed += Time.deltaTime;
                float t = Mathf.PingPong(elapsed * 6f, 1f);
                if (_renderer != null)
                {
                    _renderer.color = Color.Lerp(glowColor, _baseColor, t);
                }

                yield return null;
            }

            if (_renderer != null)
            {
                _renderer.color = _baseColor;
            }

            _levelFlashRoutine = null;
        }

        private static Sprite BuildPlayerSprite()
        {
            const int size = 64;
            var texture = new Texture2D(size, size, TextureFormat.RGBA32, false)
            {
                filterMode = FilterMode.Point
            };

            var center = new Vector2(size / 2f, size / 5f * 4f);
            var pixels = new Color[size * size];
            var body = new Color(0.2f, 0.8f, 1f, 1f);

            for (int y = 0; y < size; y++)
            {
                for (int x = 0; x < size; x++)
                {
                    int index = x + y * size;
                    float dist = Vector2.Distance(center, new Vector2(x, y * 0.9f));
                    pixels[index] = dist < size * 0.35f ? body : new Color(0f, 0f, 0f, 0f);
                }
            }

            texture.SetPixels(pixels);
            texture.Apply();

            return Sprite.Create(texture, new Rect(0, 0, size, size), new Vector2(0.5f, 0.2f), 64f);
        }
    }
}
