using UnityEngine;

/// <summary>
/// UtilityScorer — Evaluates all AI actions and returns the highest-scoring one.
/// Stateless, zero-allocation. Called by AIBrain on each decision tick.
/// 
/// Uses Response Curves for nuanced decisions rather than hard thresholds.
/// Each AIAction.Score() returns 0-1 utility; UtilityScorer scales by role weights.
/// </summary>
public class UtilityScorer
{
    // ─── Role Weight Profiles ────────────────────────────────
    public enum AIRole
    {
        Balanced,
        Collector,
        Defender,
        Aggressor
    }

    [System.Serializable]
    public struct RoleWeights
    {
        public float collectWeight;
        public float depositWeight;
        public float attackWeight;
        public float defendWeight;
        public float retreatWeight;
        public float escortWeight;
        public float interceptWeight;
        public float patrolWeight;

        public static RoleWeights Balanced => new RoleWeights
        {
            collectWeight = 1.0f,
            depositWeight = 1.0f,
            attackWeight = 1.0f,
            defendWeight = 1.2f,
            retreatWeight = 1.0f,
            escortWeight = 0.8f,
            interceptWeight = 1.0f,
            patrolWeight = 1.0f
        };

        public static RoleWeights Collector => new RoleWeights
        {
            collectWeight = 1.5f,
            depositWeight = 1.3f,
            attackWeight = 0.6f,
            defendWeight = 0.8f,
            retreatWeight = 1.2f,
            escortWeight = 0.5f,
            interceptWeight = 0.7f,
            patrolWeight = 1.0f
        };

        public static RoleWeights Defender => new RoleWeights
        {
            collectWeight = 0.6f,
            depositWeight = 0.8f,
            attackWeight = 1.2f,
            defendWeight = 2.0f,
            retreatWeight = 0.8f,
            escortWeight = 1.5f,
            interceptWeight = 1.3f,
            patrolWeight = 0.6f
        };

        public static RoleWeights Aggressor => new RoleWeights
        {
            collectWeight = 0.5f,
            depositWeight = 0.7f,
            attackWeight = 1.8f,
            defendWeight = 0.8f,
            retreatWeight = 0.6f,
            escortWeight = 0.4f,
            interceptWeight = 1.6f,
            patrolWeight = 0.5f
        };

        public static RoleWeights ForRole(AIRole role)
        {
            switch (role)
            {
                case AIRole.Collector: return Collector;
                case AIRole.Defender: return Defender;
                case AIRole.Aggressor: return Aggressor;
                default: return Balanced;
            }
        }
    }

    // ─── Pre-allocated action instances (no allocation per tick) ──
    private readonly CollectBagAction _collectAction = new CollectBagAction();
    private readonly DepositBagAction _depositAction = new DepositBagAction();
    private readonly AttackEnemyAction _attackAction = new AttackEnemyAction();
    private readonly DefendVaultAction _defendAction = new DefendVaultAction();
    private readonly PatrolAction _patrolAction = new PatrolAction();
    private readonly RetreatAction _retreatAction = new RetreatAction();
    private readonly EscortCarrierAction _escortAction = new EscortCarrierAction();
    private readonly InterceptCarrierAction _interceptAction = new InterceptCarrierAction();

    private readonly AIAction[] _allActions;
    private RoleWeights _weights;

    // ─── Decision State ──────────────────────────────────────
    private AIAction _currentAction;
    private float _currentActionScore;
    private float _minSwitchTime = 1.5f;
    private float _actionStartTime;

    // ─── Debug ───────────────────────────────────────────────
    public AIAction CurrentAction => _currentAction;
    public float CurrentActionScore => _currentActionScore;
    public string CurrentActionName => _currentAction?.Name ?? "None";

    // Expose for debug display
    private readonly float[] _lastScores = new float[8];
    public float[] LastScores => _lastScores;

    public UtilityScorer(AIRole role = AIRole.Balanced)
    {
        _weights = RoleWeights.ForRole(role);

        _allActions = new AIAction[]
        {
            _collectAction,   // 0
            _depositAction,   // 1
            _attackAction,    // 2
            _defendAction,    // 3
            _patrolAction,    // 4
            _retreatAction,   // 5
            _escortAction,    // 6
            _interceptAction  // 7
        };
    }

    /// <summary>
    /// Evaluate all actions and return the best one.
    /// Zero allocation — all actions are pre-allocated.
    /// </summary>
    public AIAction Evaluate(AIBrain brain, AIBlackboard bb, TeamBlackboard tb)
    {
        float bestScore = float.MinValue;
        AIAction bestAction = null;

        for (int i = 0; i < _allActions.Length; i++)
        {
            float rawScore = _allActions[i].Score(bb, tb);
            float weight = GetWeight(i);
            float weightedScore = rawScore * weight;

            _lastScores[i] = weightedScore; // Store for debug

            if (weightedScore > bestScore)
            {
                bestScore = weightedScore;
                bestAction = _allActions[i];
            }
        }

        // Hysteresis: don't switch unless new action is significantly better
        // (prevents oscillation between similar-scoring actions)
        if (_currentAction != null && bestAction != _currentAction)
        {
            bool timeLocked = (Time.time - _actionStartTime) < _minSwitchTime;
            bool canInterrupt = bestAction.CanInterrupt;
            bool significantlyBetter = bestScore > _currentActionScore * 1.2f;

            if (timeLocked && !canInterrupt && !significantlyBetter)
            {
                return _currentAction; // Stay with current action
            }
        }

        // Switch action
        if (bestAction != _currentAction)
        {
            _currentAction?.OnExit(brain, bb, tb);
            _currentAction = bestAction;
            _currentActionScore = bestScore;
            _actionStartTime = Time.time;
            _currentAction?.OnEnter(brain, bb, tb);
        }

        return _currentAction;
    }

    /// <summary>Force reset (e.g., on death/respawn).</summary>
    public void Reset()
    {
        _currentAction = null;
        _currentActionScore = 0f;
    }

    private float GetWeight(int actionIndex)
    {
        switch (actionIndex)
        {
            case 0: return _weights.collectWeight;
            case 1: return _weights.depositWeight;
            case 2: return _weights.attackWeight;
            case 3: return _weights.defendWeight;
            case 4: return _weights.patrolWeight;
            case 5: return _weights.retreatWeight;
            case 6: return _weights.escortWeight;
            case 7: return _weights.interceptWeight;
            default: return 1f;
        }
    }

    public void SetRole(AIRole role)
    {
        _weights = RoleWeights.ForRole(role);
    }
}
