<section class="card character-panel">
    @if (IsCampaignDataLoading)
    {
        <div class="skeleton-stack">
            <div class="skeleton-line"></div>
            <div class="skeleton-line short"></div>
            <div class="skeleton-line"></div>
        </div>
    }
    else if (SelectedCampaign is null)
    {
        <p class="empty">No campaign selected. Choose one in the header.</p>
    }
    else if (SelectedCampaign.Characters.Length == 0)
    {
        <p class="empty">No characters in this campaign yet.</p>
    }
    else
    {
        <div class="character-picker" role="tablist" aria-label="Character picker">
            @foreach (var character in SelectedCampaign.Characters)
            {
                var isSelectedCharacter = SelectedCharacterId == character.Id;
                <button type="button" class="icon-tab @(isSelectedCharacter ? "active" : string.Empty)"
                        aria-label="@character.Name" @onclick="() => CharacterSelected.InvokeAsync(character.Id)">
                    <span class="icon-tab-glyph">@InitialsFor(character.Name)</span>
                    <span class="icon-tab-text">@character.Name</span>
                </button>
            }
        </div>
        @if (SelectedCharacter is not null)
        {
            <article class="skills-section">
                <div class="section-head">
                    <button
                        type="button"
                        class="chip-button"
                        title="Edit character"
                        disabled="@(IsMutating || !CanEditCharacter(SelectedCharacter))"
                        @onclick="() => EditCharacterRequested.InvokeAsync(SelectedCharacter)">
                        <span aria-hidden="true" class="emoji">✏️</span>
                        <span class="sr-only">Edit character</span>
                    </button>
                    <h3 class="skills-heading">
                        @SelectedCharacter.Name
                        <span
                            class="muted">
                            | Owner: @OwnerLabel(SelectedCharacter.OwnerUserId) | Campaign: @SelectedCampaign.Name
                        </span>
                    </h3>
                    <div class="skill-filter-wrap">
                        <label class="sr-only" for="skill-filter-input">Filter skills</label>
                        <input id="skill-filter-input"
                               class="skill-filter-input"
                               type="search"
                               placeholder="Filter skills"
                               @bind="SkillFilterText"
                               @bind:event="oninput"/>
                    </div>
                    <div class="chip-toolbar">
                        <label class="visibility-control" for="roll-visibility">Visibility</label>
                        <select id="roll-visibility"
                                value="@(RollVisibility == "private" ? "private" : "public")"
                                @onchange="OnRollVisibilityChangedAsync">
                            <option value="public">Public</option>
                            <option value="private">Private</option>
                        </select>
                    </div>
                </div>
                @{
                    var orderedSkillGroups = SelectedCharacterSkillGroups.OrderBy(group => group.Name).ToList();
                    var filteredSkills = SelectedCharacterSkills.Where(SkillMatchesFilter).ToList();
                    var hasSkillFilter = !string.IsNullOrWhiteSpace(SkillFilterText);
                    var visibleSkillGroups = orderedSkillGroups.Where(group => !hasSkillFilter || filteredSkills.Any(skill => skill.SkillGroupId == group.Id)).ToList();
                    var ungroupedSkills = filteredSkills.Where(skill => !skill.SkillGroupId.HasValue).ToList();
                }
                @if (!hasSkillFilter && SelectedCharacterSkills.Count == 0 && orderedSkillGroups.Count == 0)
                {
                    <p class="empty">No skills for this character yet.</p>
                }
                @if (hasSkillFilter && filteredSkills.Count == 0)
                {
                    <p class="empty">No skills match the current filter.</p>
                }
                @foreach (var group in visibleSkillGroups)
                {
                    var groupSkills = filteredSkills.Where(skill => skill.SkillGroupId == group.Id).ToList();
                    <SkillGroupBlock
                        Title="@group.Name"
                        SkillGroupId="group.Id"
                        Skills="groupSkills"
                        IsMutating="IsMutating"
                        CanEditGroup="CanEditCharacter(SelectedCharacter)"
                        CanCreateSkill="CanEditCharacter(SelectedCharacter)"
                        HasSkillFilter="hasSkillFilter"
                        EmptyMessage="No skills in this group yet."
                        ShowGroupActions="true"
                        CanEditSkill="CanEditSkill"
                        SkillDefinitionLabel="SkillDefinitionLabel"
                        AddSkillRequested="OnAddSkillRequestedAsync"
                        EditSkillRequested="OnEditSkillRequestedAsync"
                        RollSkillRequested="RollSkillAsync"
                        DeleteSkillRequested="DeleteSkillAsync"
                        EditGroupRequested="OnEditSkillGroupRequestedAsync"
                        DeleteGroupRequested="DeleteSkillGroupAsync"/>
                }
                @if (!hasSkillFilter || ungroupedSkills.Count > 0)
                {
                    <SkillGroupBlock
                        Title="Ungrouped"
                        SkillGroupId="@((Guid?)null)"
                        Skills="ungroupedSkills"
                        IsMutating="IsMutating"
                        CanEditGroup="false"
                        CanCreateSkill="CanEditCharacter(SelectedCharacter)"
                        HasSkillFilter="hasSkillFilter"
                        EmptyMessage="No ungrouped skills."
                        ShowGroupActions="false"
                        CanEditSkill="CanEditSkill"
                        SkillDefinitionLabel="SkillDefinitionLabel"
                        AddSkillRequested="OnAddSkillRequestedAsync"
                        EditSkillRequested="OnEditSkillRequestedAsync"
                        RollSkillRequested="RollSkillAsync"
                        DeleteSkillRequested="DeleteSkillAsync"
                        EditGroupRequested="OnEditSkillGroupRequestedAsync"
                        DeleteGroupRequested="DeleteSkillGroupAsync"/>
                }

                <button
                    type="button"
                    class="add-row-button"
                    disabled="@(IsMutating || !CanEditCharacter(SelectedCharacter))"
                    @onclick="OpenCreateSkillGroupModal">
                    <span class="skill-create-icon" aria-hidden="true">+</span>
                    <span>Add group</span>
                </button>
            </article>
        }

        <div class="character-panel-fill" aria-hidden="true"></div>
    }
</section>

@if (ShowCreateSkillGroupModal || ShowEditSkillGroupModal)
{
    <div class="modal-overlay" role="presentation">
        <section class="modal-card" role="dialog" aria-modal="true" aria-label="Skill Group">
            <h2>@(ShowEditSkillGroupModal ? "Edit Skill Group" : "Create Skill Group")</h2>
            @if (!string.IsNullOrWhiteSpace(SkillGroupState.ErrorMessage))
            {
                <p class="form-error">@SkillGroupState.ErrorMessage</p>
            }
            <form class="form-grid" @onsubmit="@(ShowEditSkillGroupModal ? SubmitUpdateSkillGroupAsync : SubmitCreateSkillGroupAsync)" @onsubmit:preventDefault>
                <label for="skill-group-name">Group name</label>
                <input id="skill-group-name" @bind="SkillGroupState.Model.Name" @bind:event="oninput"/>
                @if (SkillGroupState.Errors.TryGetValue("name", out var groupNameError))
                {
                    <p class="field-error">@groupNameError</p>
                }

                <label for="skill-group-expression">Prototype expression</label>
                <input id="skill-group-expression" value="@SkillGroupState.Model.DiceRollDefinition" @oninput="OnSkillGroupExpressionChanged"/>
                <p class="field-help">@SkillGroupExpressionHelpText</p>
                @if (SkillGroupState.Errors.TryGetValue("diceRollDefinition", out var expressionError))
                {
                    <p class="field-error">@expressionError</p>
                }

                @if (IsD6Ruleset)
                {
                    <label for="skill-group-wild-dice">Prototype wild dice</label>
                    <input id="skill-group-wild-dice" type="number" min="1" step="1" @bind="SkillGroupState.Model.WildDice"/>
                    @if (SkillGroupState.Errors.TryGetValue("wildDice", out var wildDiceError))
                    {
                        <p class="field-error">@wildDiceError</p>
                    }

                    <label for="skill-group-allow-fumble">Prototype allow fumble</label>
                    <input id="skill-group-allow-fumble" type="checkbox" @bind="SkillGroupState.Model.AllowFumble"/>
                }
                else if (IsRolemasterRuleset)
                {
                    @if (IsSkillGroupRolemasterOpenEnded)
                    {
                        <label for="skill-group-fumble-range">Prototype fumble range</label>
                        <input id="skill-group-fumble-range" type="number" min="0" max="95" step="1" @bind="SkillGroupState.Model.FumbleRange"/>
                        <p class="field-help">Used only for open-ended percentile skills created from this group.</p>
                        @if (SkillGroupState.Errors.TryGetValue("fumbleRange", out var fumbleRangeError))
                        {
                            <p class="field-error">@fumbleRangeError</p>
                        }
                    }
                }

                @if (SkillGroupState.Errors.TryGetValue("character", out var characterError))
                {
                    <p class="field-error">@characterError</p>
                }
                @if (SkillGroupState.Errors.TryGetValue("group", out var groupError))
                {
                    <p class="field-error">@groupError</p>
                }

                <div class="inline-actions">
                    <button type="submit" disabled="@(IsMutating || IsSubmittingSkillGroup)">@(ShowEditSkillGroupModal ? "Save Group" : "Create Group")</button>
                    <button type="button" class="ghost" disabled="@(IsMutating || IsSubmittingSkillGroup)" @onclick="CloseSkillGroupModals">Cancel</button>
                </div>
            </form>
        </section>
    </div>
}

<SkillFormModal
    Visible="ShowCreateSkillModal"
    AutoFocusName="true"
    RulesetId="@SelectedCampaignRulesetId"
    Title="Create Skill"
    SubmitLabel="Create Skill"
    NameInputId="skill-create-name"
    ExpressionInputId="skill-create-expression"
    SkillGroupInputId="skill-create-group"
    WildDiceInputId="skill-create-wild-dice"
    AllowFumbleInputId="skill-create-allow-fumble"
    FumbleRangeInputId="skill-create-fumble-range"
    InitialModel="CreateSkillInitialModel"
    FormVersion="CreateSkillFormVersion"
    SelectedCharacterId="SelectedCharacterId"
    EditingSkillId="null"
    AvailableSkillGroups="SelectedCharacterSkillGroups"
    IsMutating="IsMutating"
    SkillSaved="OnSkillCreatedAsync"
    CancelRequested="CloseSkillModals"/>

<SkillFormModal
    Visible="ShowEditSkillModal"
    RulesetId="@SelectedCampaignRulesetId"
    Title="Edit Skill"
    SubmitLabel="Save Skill"
    NameInputId="skill-edit-name"
    ExpressionInputId="skill-edit-expression"
    SkillGroupInputId="skill-edit-group"
    WildDiceInputId="skill-edit-wild-dice"
    AllowFumbleInputId="skill-edit-allow-fumble"
    FumbleRangeInputId="skill-edit-fumble-range"
    InitialModel="EditSkillInitialModel"
    FormVersion="EditSkillFormVersion"
    SelectedCharacterId="SelectedCharacterId"
    EditingSkillId="EditingSkillId"
    AvailableSkillGroups="SelectedCharacterSkillGroups"
    IsMutating="IsMutating"
    SkillSaved="OnSkillUpdatedAsync"
    CancelRequested="CloseSkillModals"/>
