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

namespace Actions {
    public class ShootAction : BaseAction {
        private const float ROTATION_SPEED = 10f;
        private const float SHOOTING_STATE_TIME = .2f;
        private const float COOL_OFF_STATE_TIME = 1f;
        private const float AIMING_STATE_TIME = 1.5f;
        private const float UNIT_SHOULDER_HEIGHT = 1.7f;

        [SerializeField] private LayerMask obstaclesLayerMask;
        private bool canShootBullet;

        private State state;
        private float stateTimer;
        public Unit TargetUnit { get; private set; }
        public Unit ShootingUnit { get; private set; }
        public int MaxShootDistance { get; private set; }

        protected override void Awake() {
            base.Awake();
            MaxShootDistance = 7;
            ActionName = "Shoot";
        }

        private void Update() {
            if (!IsActive) return;
            stateTimer -= Time.deltaTime;

            switch (state) {
                case State.Aiming:
                    Vector3 aimDirection = (TargetUnit.GetWorldPosition() - Unit.GetWorldPosition()).normalized;
                    aimDirection.y = 0f;
                    transform.forward = Vector3.Slerp(transform.forward, aimDirection, ROTATION_SPEED * Time.deltaTime);
                    break;
                case State.Shooting:
                    if (canShootBullet) {
                        Shoot();
                        canShootBullet = false;
                    }

                    break;
                case State.Cooloff:
                    break;
            }

            if (stateTimer <= 0f) NextState();
        }


        public event EventHandler<ShootEventArgs> OnShoot;
        public static event EventHandler<ShootEventArgs> OnAnyShoot;

        private void Shoot() {
            OnAnyShoot?.Invoke(this, new() { TargetUnit = TargetUnit, ShootingUnit = Unit });
            OnShoot?.Invoke(this, new() { TargetUnit = TargetUnit, ShootingUnit = Unit });
            TargetUnit.Damage(40);
        }

        private void NextState() {
            switch (state) {
                case State.Aiming:
                    state = State.Shooting;
                    stateTimer = SHOOTING_STATE_TIME;
                    break;
                case State.Shooting:
                    state = State.Cooloff;
                    stateTimer = COOL_OFF_STATE_TIME;
                    break;
                case State.Cooloff:
                    ActionComplete();
                    break;
            }
        }

        public override void TakeAction(GridPosition gridPosition, Action onActionComplete) {
            IsActive = true;

            TargetUnit = LevelGrid.Instance.GetUnitAtGridPosition(gridPosition);
            canShootBullet = true;

            state = State.Aiming;
            stateTimer = AIMING_STATE_TIME;

            ActionStart(onActionComplete);
        }

        public override List<GridPosition> GetValidActionGridPositionList() => GetValidActionGridPositionList(Unit.GridPosition);

        private List<GridPosition> GetValidActionGridPositionList(GridPosition unitGridPosition) {
            List<GridPosition> validGridPositionList = new();
            for (int xPosition = -MaxShootDistance; xPosition <= MaxShootDistance; xPosition++) {
                for (int zPosition = -MaxShootDistance; zPosition <= MaxShootDistance; zPosition++) {
                    for (int floor = -MaxShootDistance; floor < MaxShootDistance; floor++) {
                        GridPosition offsetGridPosition = new(xPosition, zPosition, floor);
                        GridPosition testGridPosition = unitGridPosition + offsetGridPosition;

                        if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition)) continue; //Only return valid grid positions
                        if (Mathf.Abs(xPosition) + Mathf.Abs(zPosition) > MaxShootDistance) continue; //Out of Range

                        if (!LevelGrid.Instance.HasAnyUnitOnGridPosition(testGridPosition)) continue; //Grid position is empty, no unit

                        Unit unitAtGridPosition = LevelGrid.Instance.GetUnitAtGridPosition(testGridPosition);
                        if (unitAtGridPosition.IsEnemy == Unit.IsEnemy) continue; //Both units are on the same 'team'
                        TargetUnit = unitAtGridPosition;

                        Vector3 unitWorldPosition = LevelGrid.Instance.GetWorldPosition(unitGridPosition);
                        Vector3 shootDir = (TargetUnit.GetWorldPosition() - unitWorldPosition).normalized;
                        if (Physics.Raycast(unitWorldPosition + Vector3.up * UNIT_SHOULDER_HEIGHT, shootDir, Vector3.Distance(unitWorldPosition, TargetUnit.GetWorldPosition()), obstaclesLayerMask)) continue; // Blocked by an obstacle

                        validGridPositionList.Add(testGridPosition);
                    }
                }
            }

            return validGridPositionList;
        }

        protected override EnemyAIAction GetEnemyAIAction(GridPosition gridPosition) {
            Unit targetUnit = LevelGrid.Instance.GetUnitAtGridPosition(gridPosition);
            return new(gridPosition, 100 + Mathf.RoundToInt((1 - targetUnit.HealthSystem.HealthNormalized) * 100f));
        }

        public int GetTargetCountAtPosition(GridPosition gridPosition) => GetValidActionGridPositionList(gridPosition).Count;

        private enum State {
            Aiming,
            Shooting,
            Cooloff
        }

        public class ShootEventArgs : EventArgs {
            public Unit ShootingUnit;
            public Unit TargetUnit;
        }
    }
}