<?php
/**
 * ════════════════════════════════════════════════════════════════════
 * Trade Grading — shared trade_classification → letter-grade constants
 * ────────────────────────────────────────────────────────────────────
 * Extracted 2026-08-08 (docs/architecture/SINGLE_SOURCE_OF_TRUTH_AUDIT.md
 * §9, Consolidated #15/#16 — Wave 4d of the migration plan). Before this
 * file existed, the exact same A+/A/B/C mapping was hand-duplicated
 * verbatim in public/reports.php's $fsGradeMap and
 * TraderProfileBehaviorEngine::GRADE_MAP — currently identical, but with
 * zero shared-constant mechanism preventing future drift. This is now the
 * one place that mapping is declared; both callers reference it instead
 * of maintaining their own copy.
 *
 * Source of the mapping itself: trades.trade_classification's real,
 * CHECK-constrained values (src/TradeClassificationEngine.php is the
 * sole write path as of 2026-08-18, replacing the retired
 * sp_classify_trade stored procedure — see CLAUDE.md) —
 * disciplined_win / controlled_loss / breakeven / elevated_win /
 * loss_high_risk / tilt_warning / revenge_trade. A+/A/B/C grading was
 * Kevin's own confirmed scheme (2026-07-26), not invented here. The
 * 7-value SET is unchanged by the 2026-08-18 fix — only the trigger
 * logic behind each value changed (Active Coaching's tier-recommended
 * risk %, not a planned-vs-actual/loss-streak check) — so this mapping
 * itself needed no update.
 *
 * TWO DELIBERATELY DIFFERENT "undisciplined" sets — the audit found these
 * were previously an accidental-looking 3-value-vs-4-value split with no
 * documentation of WHY they differ. Named explicitly here as two distinct,
 * intentional constants rather than one being an unlabeled variant of the
 * other:
 *   - UNDISCIPLINED_FOR_GRADING (3 values) — matches GRADE_MAP's own "C"
 *     bucket exactly. Used wherever the question is "does this trade fall
 *     into the worst letter grade" (reports.php's C-bucket, this file's
 *     own isCGrade()-style checks, Trader Profile's Growth Timeline SQL).
 *     elevated_win is deliberately excluded — it grades B, a "minor
 *     deviation," not the worst bucket.
 *   - UNDISCIPLINED_FOR_SEVERITY_CAP (4 values) — TradingArchiveEngine's
 *     own set, used for star-rating/severity-cap purposes where an
 *     elevated (over-risked) win is still real evidence of undisciplined
 *     position sizing, even though the trade itself won.
 * ════════════════════════════════════════════════════════════════════
 */

class TradeGrading
{
    public const GRADE_MAP = [
        'disciplined_win' => 'A+',
        'controlled_loss' => 'A', 'breakeven' => 'A',
        'elevated_win' => 'B',
        'loss_high_risk' => 'C', 'tilt_warning' => 'C', 'revenge_trade' => 'C',
    ];

    public const UNDISCIPLINED_FOR_GRADING = ['loss_high_risk', 'tilt_warning', 'revenge_trade'];

    public const UNDISCIPLINED_FOR_SEVERITY_CAP = ['elevated_win', 'loss_high_risk', 'tilt_warning', 'revenge_trade'];

    public static function isCGrade(?string $classification): bool
    {
        return isset(self::GRADE_MAP[$classification]) && self::GRADE_MAP[$classification] === 'C';
    }
}
