using Theriapolis.Core.Rules.Stats;

namespace Theriapolis.Core.Rules.Character;

/// <summary>
/// Pure data describing the *deltas* a level-up produces. Phase 6.5 M0:
/// <see cref="LevelUpFlow"/> computes one of these from
/// <c>(character, targetLevel, levelUpSeed)</c>; the player confirms; then
/// <see cref="Character.ApplyLevelUp"/> applies it.
///
/// Splitting compute from apply keeps the level-up screen previewable
/// (the player sees the rolled HP and feature list before committing) and
/// makes mid-flight save/load deterministic — the same seed always produces
/// the same payload.
/// </summary>
public sealed class LevelUpResult
{
    /// <summary>The level being advanced *to*. After Apply, <c>Character.Level == NewLevel</c>.</summary>
    public int NewLevel { get; init; }

    /// <summary>HP gained on this level-up. Already incorporates CON modifier.</summary>
    public int HpGained { get; init; }

    /// <summary>Average-rounded-up HP value used (for "take average" path); rolled value used otherwise.</summary>
    public int HpHitDieResult { get; init; }

    /// <summary>True if the player picked the "take average" option; false if rolled.</summary>
    public bool HpWasAveraged { get; init; }

    /// <summary>Class feature ids unlocked at this level (per <see cref="Data.ClassDef.LevelTable"/>).</summary>
    public string[] ClassFeaturesUnlocked { get; init; } = Array.Empty<string>();

    /// <summary>
    /// Subclass feature ids unlocked at this level (post-L3, when SubclassId
    /// is set). Empty for pre-subclass and non-subclass-feature levels.
    /// </summary>
    public string[] SubclassFeaturesUnlocked { get; init; } = Array.Empty<string>();

    /// <summary>True if this level grants a subclass selection slot (level 3 by default).</summary>
    public bool GrantsSubclassChoice { get; init; }

    /// <summary>True if this level grants an Ability Score Improvement choice (levels 4 / 8 / 12 / 16 / 19).</summary>
    public bool GrantsAsiChoice { get; init; }

    /// <summary>The proficiency bonus *after* this level-up.</summary>
    public int NewProficiencyBonus { get; init; }
}

/// <summary>
/// The player's choices at level-up that need confirmation before
/// <see cref="Character.ApplyLevelUp"/> commits the deltas. Leave fields
/// null/empty when the corresponding slot isn't open at this level.
/// </summary>
public sealed class LevelUpChoices
{
    /// <summary>
    /// At level 3 (or whatever <see cref="C.SUBCLASS_SELECTION_LEVEL"/>
    /// becomes), the player picks a subclass id. Must reference one of
    /// <c>character.ClassDef.SubclassIds</c>.
    /// </summary>
    public string? SubclassId { get; set; }

    /// <summary>
    /// At ASI levels (<see cref="C.ASI_LEVELS"/>), the player picks ability
    /// score improvements. Either:
    /// - one ability +2 (cap at <see cref="C.ABILITY_SCORE_CAP_PRE_L20"/>)
    /// - two abilities +1 each (each cap at <see cref="C.ABILITY_SCORE_CAP_PRE_L20"/>)
    /// </summary>
    public Dictionary<AbilityId, int> AsiAdjustments { get; set; } = new();

    /// <summary>
    /// True if the player picked "take average HP" instead of "roll".
    /// Default is "average" — predictable, avoids the dump-stat-roll problem.
    /// </summary>
    public bool TakeAverageHp { get; set; } = true;
}
