import { clamp } from "../../../../core/math/clamp.js";
import { AnimationCurve } from "../../../animation/curve/AnimationCurve.js";
import { Keyframe } from "../../../animation/curve/Keyframe.js";
import { DecisionPoint } from "./DecisionPoint.js";
import { MasteryEvaluator } from "./MasteryEvaluator.js";

/**
 * Default symmetric foot-asymmetry curve. Input is signed alignment
 * ∈ [-1, +1]:
 *   +1 = optimal (turn direction matches the standing-foot's push-off side)
 *    0 = no turn, or no relevant standing foot
 *   −1 = anti-optimal (turning AWAY from the push-off direction)
 *
 * Asymmetric magnitude per the design rule: +5% peak bonus vs −2% trough.
 */
export function makeDefaultFootAsymmetryTurnCurve() {
    return AnimationCurve.from([
        Keyframe.from(-1.0, 0.98),  // anti-aligned: −2% accel
        Keyframe.from(0.0, 1.00),
        Keyframe.from(1.0, 1.05),   // aligned: +5% accel
    ]);
}

/**
 * Mastery evaluator: ground acceleration is modulated by which foot is
 * standing relative to which way you're turning.
 *
 * Physically motivated: when you turn while running, you push off the
 * grounded foot to redirect momentum. The most powerful push-off comes
 * from the LEFT foot when turning right, and the RIGHT foot when
 * turning left. (Think of how runners plant on the outside leg before
 * cutting in the opposite direction.) Players who time their turns to
 * align with the right foot's swing/stance phase carry more momentum
 * through the turn.
 *
 * The bonus is small (max ~5%) by design — it's mastery, not magic.
 * Compounded over a long turn under sprint, it accumulates into
 * meaningfully more distance covered.
 *
 * Engineering note: we read `runtime.yawRateRadPerSec` (computed during
 * look consumption) rather than recomputing from yaw deltas. That keeps
 * the evaluator simple and avoids assumptions about controller internals.
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export class FootAsymmetryTurnEvaluator extends MasteryEvaluator {
    constructor() {
        super();
        this.name = "FootAsymmetryTurn";

        /**
         * Alignment ∈ [-1, +1] → multiplier curve. Symmetric in behaviour
         * (both sides exist) and asymmetric in magnitude (bonus side
         * larger than penalty side).
         * @type {AnimationCurve}
         */
        this.curve = makeDefaultFootAsymmetryTurnCurve();

        /**
         * Exponent applied to (curve - 1.0). Scales overall magnitude
         * while preserving symmetry around 1.0. Default 1.0 = use curve
         * as authored.
         * @type {number}
         */
        this.flatBoost = 1.0;

        /**
         * Yaw rate magnitude (rad/s) at which alignment saturates to ±1.
         * Slow turns produce weaker alignment; fast turns saturate. ~1.5
         * rad/s ≈ 86°/s — a brisk but not-flicky turn. Sub-second
         * 180° turns saturate.
         * @type {number}
         */
        this.saturationYawRate = 1.5;
    }

    evaluate(decisionPoint, controller, runtime) {
        if (decisionPoint !== DecisionPoint.GroundAccel) return 1.0;
        // Only meaningful when grounded and actually moving.
        if (!controller.state.grounded) return 1.0;
        if (controller.state.speed < 0.5) return 1.0;

        const yawRate = runtime.yawRateRadPerSec;
        if (!yawRate || Math.abs(yawRate) < 1e-4) return 1.0; // not turning

        // Sign convention: in our engine, yawRate < 0 means turning RIGHT
        // (mouse-right decreases bodyYaw). Encode turn direction as +1
        // for right, -1 for left.
        const turnSign = yawRate < 0 ? 1 : -1;

        // Optimal-foot encoding: standing on the LEFT foot is good for
        // RIGHT turns (you push off the left to swing right); standing on
        // the RIGHT foot is good for LEFT turns.
        const footSign = runtime.standingFoot === "L" ? 1 : -1;

        // Yaw intensity: 0 (no turn) to 1 (saturated). Smooth ramp.
        const intensity = clamp(
            Math.abs(yawRate) / Math.max(this.saturationYawRate, 1e-4),
            0, 1,
        );

        // Alignment ∈ [-1, +1].  Aligned when turnSign × footSign > 0.
        const alignment = turnSign * footSign * intensity;

        const raw = this.curve.evaluate(alignment);
        return 1.0 + (raw - 1.0) * this.flatBoost;
    }
}
