namespace RpgRoller.Tests;

public sealed class ServiceHelperExtractionTests
{
    [Fact]
    public void RoleSerializer_NormalizesParsesAndSerializesRoles()
    {
        var normalized = RoleSerializer.Normalize([" Admin ", "gm", "admin", "", "GM"]);
        var serialized = RoleSerializer.Serialize(normalized);
        var parsed = RoleSerializer.Parse(" admin,GM,admin ");

        Assert.Equal(["admin", "gm"], normalized);
        Assert.Equal("admin,gm", serialized);
        Assert.Equal(["admin", "gm"], parsed);
        Assert.True(RoleSerializer.HasRole(serialized, "ADMIN"));
        Assert.False(RoleSerializer.HasRole(serialized, "owner"));
    }

    [Theory]
    [InlineData("public", RollVisibility.Public)]
    [InlineData("PRIVATE", RollVisibility.Private)]
    public void RollVisibilityParser_ParsesKnownValues(string input, RollVisibility expected)
    {
        var result = RollVisibilityParser.Parse(input);

        Assert.True(result.Succeeded);
        Assert.Equal(expected, result.Value);
    }

    [Fact]
    public void RollVisibilityParser_RejectsUnknownValue()
    {
        var result = RollVisibilityParser.Parse("hidden");

        Assert.False(result.Succeeded);
        Assert.Equal("invalid_visibility", result.Error!.Code);
    }

    [Fact]
    public void CustomRollOptionsResolver_ReturnsD6DefaultsOnlyForD6()
    {
        Assert.Equal((1, true, (int?)null), CustomRollOptionsResolver.Resolve(RulesetKind.D6));
        Assert.Equal((0, false, (int?)null), CustomRollOptionsResolver.Resolve(RulesetKind.Dnd5e));
        Assert.Equal((0, false, (int?)null), CustomRollOptionsResolver.Resolve(RulesetKind.Rolemaster));
    }

    [Fact]
    public void SkillDefinitionValidator_ValidatesRulesetSpecificOptions()
    {
        var d6 = SkillDefinitionValidator.Validate(RulesetKind.D6, "2D+1", 1, true, null);
        var rolemaster = SkillDefinitionValidator.Validate(RulesetKind.Rolemaster, "d100!+15", 0, false, 5, true);
        var invalidD6 = SkillDefinitionValidator.Validate(RulesetKind.D6, "2D+1", 0, true, null);
        var invalidRolemaster = SkillDefinitionValidator.Validate(RulesetKind.Rolemaster, "d100!+15", 0, false, null);
        var invalidRetry = SkillDefinitionValidator.Validate(RulesetKind.Rolemaster, "d100+15", 0, false, null, true);

        Assert.True(d6.Succeeded);
        Assert.Equal(("2D+1", 1, true, (int?)null, false), d6.Value);

        Assert.True(rolemaster.Succeeded);
        Assert.Equal(("d100!+15", 0, false, (int?)5, true), rolemaster.Value);

        Assert.False(invalidD6.Succeeded);
        Assert.Equal("invalid_wild_dice", invalidD6.Error!.Code);

        Assert.False(invalidRolemaster.Succeeded);
        Assert.Equal("invalid_fumble_range", invalidRolemaster.Error!.Code);

        Assert.False(invalidRetry.Succeeded);
        Assert.Equal("invalid_rolemaster_retry", invalidRetry.Error!.Code);
    }

    [Fact]
    public void RolemasterRetryPolicy_ResolvesRetryBandsAndMarkers()
    {
        Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(76));
        Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(90));
        Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(91));
        Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(110));
        Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(75));
        Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(111));
        Assert.Equal(5, RolemasterRetryPolicy.TryExtractRetryBonus("68+10=78; retry(+5): 42+10=52; final=57"));
        Assert.Equal(10, RolemasterRetryPolicy.TryExtractRetryBonus("90+1=91; retry(+10): 32+1=33; final=43"));
        Assert.Null(RolemasterRetryPolicy.TryExtractRetryBonus("68+10=78"));
    }
}