using UnityEngine;

/// <summary>
/// AIAction — Abstract base class for all AI actions.
/// Each action knows how to score itself (utility) and execute.
/// Stateless scoring — all context comes from AIBlackboard.
/// 
/// Subclasses: CollectBagAction, DepositBagAction, AttackEnemyAction,
///             DefendVaultAction, PatrolAction, RetreatAction,
///             EscortCarrierAction, InterceptCarrierAction
/// </summary>
public abstract class AIAction
{
    /// <summary>Display name for debug.</summary>
    public abstract string Name { get; }

    /// <summary>
    /// Evaluate how desirable this action is (0-1 range, higher = more desirable).
    /// Called every AI decision tick. Must be allocation-free.
    /// </summary>
    /// <param name="blackboard">Per-AI knowledge store</param>
    /// <param name="teamBoard">Shared team knowledge</param>
    /// <returns>Utility score 0-1</returns>
    public abstract float Score(AIBlackboard blackboard, TeamBlackboard teamBoard);

    /// <summary>
    /// Execute this action for one tick.
    /// Called every AI tick while this is the active action.
    /// </summary>
    /// <param name="brain">The AI brain executing this action</param>
    /// <param name="blackboard">Per-AI knowledge</param>
    /// <param name="teamBoard">Team knowledge</param>
    public abstract void Execute(AIBrain brain, AIBlackboard blackboard, TeamBlackboard teamBoard);

    /// <summary>Called when this action becomes active (optional setup).</summary>
    public virtual void OnEnter(AIBrain brain, AIBlackboard blackboard, TeamBlackboard teamBoard) { }

    /// <summary>Called when this action is replaced by another (optional cleanup).</summary>
    public virtual void OnExit(AIBrain brain, AIBlackboard blackboard, TeamBlackboard teamBoard) { }

    /// <summary>
    /// Whether this action can interrupt the current action.
    /// High-priority actions (defend vault, retreat) return true.
    /// </summary>
    public virtual bool CanInterrupt => false;

    // ─── Utility Helpers ─────────────────────────────────────

    /// <summary>Smooth curve: high when value is low, low when value is high.</summary>
    protected float InverseCurve(float value, float max)
    {
        return Mathf.Clamp01(1f - (value / max));
    }

    /// <summary>Smooth curve: high when value is high.</summary>
    protected float LinearCurve(float value, float max)
    {
        return Mathf.Clamp01(value / max);
    }

    /// <summary>Step function: 1 if above threshold, 0 otherwise.</summary>
    protected float StepAbove(float value, float threshold)
    {
        return value >= threshold ? 1f : 0f;
    }

    /// <summary>Step function: 1 if below threshold, 0 otherwise.</summary>
    protected float StepBelow(float value, float threshold)
    {
        return value < threshold ? 1f : 0f;
    }
}
