using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;
using RpgRoller.Contracts;

namespace RpgRoller.Components.Pages.HomeControls;

[ExcludeFromCodeCoverage]
public partial class CharacterPanel
{
    private void OpenCreateSkillModal(Guid? skillGroupId = null)
    {
        var selectedGroup = skillGroupId.HasValue
            ? SelectedCharacterSkillGroups.FirstOrDefault(group => group.Id == skillGroupId.Value)
            : null;

        CreateSkillInitialModel = new()
        {
            Name = string.Empty,
            RulesetId = SelectedCampaignRulesetId,
            DiceRollDefinition = selectedGroup?.DiceRollDefinition ?? string.Empty,
            SkillGroupId = selectedGroup?.Id.ToString() ?? string.Empty,
            WildDice = selectedGroup?.WildDice ?? (IsD6Ruleset ? 1 : 0),
            AllowFumble = selectedGroup?.AllowFumble ?? IsD6Ruleset,
            FumbleRange = selectedGroup?.FumbleRange,
            RolemasterAutoRetry = false
        };

        if (IsRolemasterRuleset && string.IsNullOrWhiteSpace(CreateSkillInitialModel.DiceRollDefinition))
            CreateSkillInitialModel.DiceRollDefinition = "d100";

        CreateSkillFormVersion++;
        ShowCreateSkillModal = true;
    }

    private void OpenEditSkillModal(CharacterSheetSkill skill)
    {
        EditingSkillId = skill.Id;
        EditSkillInitialModel = new()
        {
            Name = skill.Name,
            RulesetId = SelectedCampaignRulesetId,
            DiceRollDefinition = skill.DiceRollDefinition,
            SkillGroupId = skill.SkillGroupId?.ToString() ?? string.Empty,
            WildDice = skill.WildDice,
            AllowFumble = skill.AllowFumble,
            FumbleRange = skill.FumbleRange,
            RolemasterAutoRetry = skill.RolemasterAutoRetry
        };

        EditSkillFormVersion++;
        ShowEditSkillModal = true;
    }

    private void CloseSkillModals()
    {
        ShowCreateSkillModal = false;
        ShowEditSkillModal = false;
        EditingSkillId = null;
    }

    private async Task OnSkillCreatedAsync(Guid skillId)
    {
        CloseSkillModals();
        await SkillCreated.InvokeAsync(skillId);
    }

    private async Task OnSkillUpdatedAsync(Guid skillId)
    {
        CloseSkillModals();
        await SkillUpdated.InvokeAsync(skillId);
    }

    private async Task OnRollVisibilityChangedAsync(ChangeEventArgs args)
    {
        var selectedVisibility = args.Value?.ToString() ?? "public";
        await RollVisibilityChanged.InvokeAsync(selectedVisibility);
    }

    private async Task RollSkillAsync(CharacterSheetSkill skill)
    {
        await RollRequested.InvokeAsync(skill);
    }

    private Task OnAddSkillRequestedAsync(Guid? skillGroupId)
    {
        OpenCreateSkillModal(skillGroupId);
        return Task.CompletedTask;
    }

    private Task OnEditSkillRequestedAsync(CharacterSheetSkill skill)
    {
        OpenEditSkillModal(skill);
        return Task.CompletedTask;
    }

    private Task OnEditSkillGroupRequestedAsync(Guid skillGroupId)
    {
        var skillGroup = SelectedCharacterSkillGroups.FirstOrDefault(group => group.Id == skillGroupId);
        if (skillGroup is not null)
            OpenEditSkillGroupModal(skillGroup);

        return Task.CompletedTask;
    }

    private void OpenCreateSkillGroupModal()
    {
        SkillGroupState.Model.Name = string.Empty;
        SkillGroupState.Model.RulesetId = SelectedCampaignRulesetId;
        SkillGroupState.Model.DiceRollDefinition = string.Empty;
        SkillGroupState.Model.WildDice = IsD6Ruleset ? 1 : 0;
        SkillGroupState.Model.AllowFumble = IsD6Ruleset;
        SkillGroupState.Model.FumbleRange = null;
        if (IsRolemasterRuleset)
            SkillGroupState.Model.DiceRollDefinition = "d100";
        SkillGroupState.ResetValidation();
        ShowCreateSkillGroupModal = true;
    }

    private void OpenEditSkillGroupModal(CharacterSheetSkillGroup skillGroup)
    {
        EditingSkillGroupId = skillGroup.Id;
        SkillGroupState.Model.Name = skillGroup.Name;
        SkillGroupState.Model.RulesetId = SelectedCampaignRulesetId;
        SkillGroupState.Model.DiceRollDefinition = skillGroup.DiceRollDefinition;
        SkillGroupState.Model.WildDice = skillGroup.WildDice;
        SkillGroupState.Model.AllowFumble = skillGroup.AllowFumble;
        SkillGroupState.Model.FumbleRange = skillGroup.FumbleRange;
        NormalizeSkillGroupFumbleRange();
        SkillGroupState.ResetValidation();
        ShowEditSkillGroupModal = true;
    }

    private void CloseSkillGroupModals()
    {
        ShowCreateSkillGroupModal = false;
        ShowEditSkillGroupModal = false;
        EditingSkillGroupId = null;
        SkillGroupState.ResetValidation();
    }

    private async Task SubmitCreateSkillGroupAsync()
    {
        SkillGroupState.ResetValidation();

        if (string.IsNullOrWhiteSpace(SkillGroupState.Model.Name))
            SkillGroupState.Errors["name"] = "Skill group name is required.";

        if (string.IsNullOrWhiteSpace(SkillGroupState.Model.DiceRollDefinition))
            SkillGroupState.Errors["diceRollDefinition"] = "Expression is required.";

        if (IsD6Ruleset && SkillGroupState.Model.WildDice < 1)
            SkillGroupState.Errors["wildDice"] = "D6 groups require at least one wild die.";

        if (IsRolemasterRuleset)
        {
            if (IsSkillGroupRolemasterOpenEnded && !SkillGroupState.Model.FumbleRange.HasValue)
                SkillGroupState.Errors["fumbleRange"] = "Open-ended Rolemaster groups require a fumble range.";
        }
        else
            SkillGroupState.Model.FumbleRange = null;

        if (!IsD6Ruleset)
        {
            SkillGroupState.Model.WildDice = 0;
            SkillGroupState.Model.AllowFumble = false;
        }

        if (!SelectedCharacterId.HasValue)
            SkillGroupState.Errors["character"] = "Select a character first.";

        if (SkillGroupState.Errors.Count > 0)
        {
            SkillGroupState.ErrorMessage = "Resolve validation issues before submitting.";
            return;
        }

        IsSubmittingSkillGroup = true;
        try
        {
            var selectedCharacterId = SelectedCharacterId!.Value;
            var createdGroup = await ApiClient.RequestAsync<SkillGroupSummary>("POST",
                $"/api/characters/{selectedCharacterId}/skill-groups",
                new CreateSkillGroupRequest(SkillGroupState.Model.Name.Trim(),
                    SkillGroupState.Model.DiceRollDefinition.Trim(), SkillGroupState.Model.WildDice,
                    SkillGroupState.Model.AllowFumble, SkillGroupState.Model.FumbleRange));
            CloseSkillGroupModals();
            await SkillGroupCreated.InvokeAsync(createdGroup.Id);
        }
        catch (ApiRequestException ex)
        {
            SkillGroupState.ErrorMessage = ex.Message;
        }
        finally
        {
            IsSubmittingSkillGroup = false;
        }
    }

    private async Task SubmitUpdateSkillGroupAsync()
    {
        SkillGroupState.ResetValidation();

        if (string.IsNullOrWhiteSpace(SkillGroupState.Model.Name))
            SkillGroupState.Errors["name"] = "Skill group name is required.";

        if (string.IsNullOrWhiteSpace(SkillGroupState.Model.DiceRollDefinition))
            SkillGroupState.Errors["diceRollDefinition"] = "Expression is required.";

        if (IsD6Ruleset && SkillGroupState.Model.WildDice < 1)
            SkillGroupState.Errors["wildDice"] = "D6 groups require at least one wild die.";

        if (IsRolemasterRuleset)
        {
            if (IsSkillGroupRolemasterOpenEnded && !SkillGroupState.Model.FumbleRange.HasValue)
                SkillGroupState.Errors["fumbleRange"] = "Open-ended Rolemaster groups require a fumble range.";
        }
        else
            SkillGroupState.Model.FumbleRange = null;

        if (!IsD6Ruleset)
        {
            SkillGroupState.Model.WildDice = 0;
            SkillGroupState.Model.AllowFumble = false;
        }

        if (!EditingSkillGroupId.HasValue)
            SkillGroupState.Errors["group"] = "Select a skill group first.";

        if (SkillGroupState.Errors.Count > 0)
        {
            SkillGroupState.ErrorMessage = "Resolve validation issues before submitting.";
            return;
        }

        IsSubmittingSkillGroup = true;
        try
        {
            var editingSkillGroupId = EditingSkillGroupId!.Value;
            var updatedGroup = await ApiClient.RequestAsync<SkillGroupSummary>("PUT",
                $"/api/skill-groups/{editingSkillGroupId}",
                new UpdateSkillGroupRequest(SkillGroupState.Model.Name.Trim(),
                    SkillGroupState.Model.DiceRollDefinition.Trim(), SkillGroupState.Model.WildDice,
                    SkillGroupState.Model.AllowFumble, SkillGroupState.Model.FumbleRange));
            CloseSkillGroupModals();
            await SkillGroupUpdated.InvokeAsync(updatedGroup.Id);
        }
        catch (ApiRequestException ex)
        {
            SkillGroupState.ErrorMessage = ex.Message;
        }
        finally
        {
            IsSubmittingSkillGroup = false;
        }
    }

    private async Task DeleteSkillAsync(Guid skillId)
    {
        try
        {
            await ApiClient.RequestAsync<bool>("DELETE", $"/api/skills/{skillId}");
            await SkillDeleted.InvokeAsync(skillId);
        }
        catch (ApiRequestException ex)
        {
            await ErrorOccurred.InvokeAsync(ex.Message);
        }
    }

    private async Task DeleteSkillGroupAsync(Guid skillGroupId)
    {
        try
        {
            await ApiClient.RequestAsync<bool>("DELETE", $"/api/skill-groups/{skillGroupId}");
            await SkillGroupDeleted.InvokeAsync(skillGroupId);
        }
        catch (ApiRequestException ex)
        {
            await ErrorOccurred.InvokeAsync(ex.Message);
        }
    }

    private bool SkillMatchesFilter(CharacterSheetSkill skill)
    {
        if (string.IsNullOrWhiteSpace(SkillFilterText))
            return true;

        var filter = SkillFilterText.Trim();
        return skill.Name.Contains(filter, StringComparison.OrdinalIgnoreCase) ||
               skill.DiceRollDefinition.Contains(filter, StringComparison.OrdinalIgnoreCase);
    }

    private static string InitialsFor(string value)
    {
        var words = value.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (words.Length == 0)
            return "?";

        if (words.Length == 1)
            return words[0].Length >= 2 ? words[0][..2].ToUpperInvariant() : words[0].ToUpperInvariant();

        return string.Concat(words[0][0], words[1][0]).ToUpperInvariant();
    }

    private void OnSkillGroupExpressionChanged(ChangeEventArgs args)
    {
        SkillGroupState.Model.DiceRollDefinition = args.Value?.ToString() ?? string.Empty;
        if (IsRolemasterRuleset)
            NormalizeSkillGroupFumbleRange();
    }

    private void NormalizeSkillGroupFumbleRange()
    {
        if (!IsRolemasterRuleset)
        {
            SkillGroupState.Model.FumbleRange = null;
            return;
        }

        if (IsSkillGroupRolemasterOpenEnded)
        {
            SkillGroupState.Model.FumbleRange ??= 5;
            return;
        }

        SkillGroupState.Model.FumbleRange = null;
    }

    private bool IsD6Ruleset => RulesetFormHelpers.IsD6(SelectedCampaignRulesetId);
    private bool IsRolemasterRuleset => RulesetFormHelpers.IsRolemaster(SelectedCampaignRulesetId);

    private bool IsSkillGroupRolemasterOpenEnded =>
        RulesetFormHelpers.IsRolemasterOpenEndedExpression(SkillGroupState.Model.DiceRollDefinition);

    private string SkillGroupExpressionHelpText => IsRolemasterRuleset
        ? $"{RulesetFormHelpers.RolemasterExampleText()}. Negative modifiers are allowed."
        : "Enter the default expression for skills created in this group.";

    private bool ShowCreateSkillModal { get; set; }
    private bool ShowEditSkillModal { get; set; }
    private bool ShowCreateSkillGroupModal { get; set; }
    private bool ShowEditSkillGroupModal { get; set; }
    private Guid? EditingSkillId { get; set; }
    private Guid? EditingSkillGroupId { get; set; }
    private SkillFormModel CreateSkillInitialModel { get; set; } = new();
    private SkillFormModel EditSkillInitialModel { get; set; } = new();
    private FormState<SkillGroupFormModel> SkillGroupState { get; } = new();
    private int CreateSkillFormVersion { get; set; }
    private int EditSkillFormVersion { get; set; }
    private bool IsSubmittingSkillGroup { get; set; }
    private string SkillFilterText { get; set; } = string.Empty;

    [Inject] private RpgRollerApiClient ApiClient { get; set; } = null!;

    [Parameter] public bool IsCampaignDataLoading { get; set; }

    [Parameter] public CampaignRoster? SelectedCampaign { get; set; }

    [Parameter] public Guid? SelectedCharacterId { get; set; }

    [Parameter] public CharacterSummary? SelectedCharacter { get; set; }

    [Parameter] public bool IsMutating { get; set; }

    [Parameter] public IReadOnlyList<CharacterSheetSkill> SelectedCharacterSkills { get; set; } = [];

    [Parameter] public IReadOnlyList<CharacterSheetSkillGroup> SelectedCharacterSkillGroups { get; set; } = [];

    [Parameter] public string SelectedCampaignRulesetId { get; set; } = string.Empty;

    [Parameter] public string RollVisibility { get; set; } = "public";

    [Parameter] public EventCallback<string> RollVisibilityChanged { get; set; }

    [Parameter] public Func<Guid, string> OwnerLabel { get; set; } = _ => string.Empty;

    [Parameter] public Func<CharacterSheetSkill, string> SkillDefinitionLabel { get; set; } = _ => string.Empty;

    [Parameter] public Func<CharacterSummary, bool> CanEditCharacter { get; set; } = _ => false;

    [Parameter] public Func<CharacterSheetSkill, bool> CanEditSkill { get; set; } = _ => false;

    [Parameter] public EventCallback<Guid> CharacterSelected { get; set; }

    [Parameter] public EventCallback<CharacterSummary> EditCharacterRequested { get; set; }

    [Parameter] public EventCallback<Guid> SkillCreated { get; set; }

    [Parameter] public EventCallback<Guid> SkillUpdated { get; set; }

    [Parameter] public EventCallback<Guid> SkillGroupCreated { get; set; }

    [Parameter] public EventCallback<Guid> SkillGroupUpdated { get; set; }

    [Parameter] public EventCallback<Guid> SkillDeleted { get; set; }

    [Parameter] public EventCallback<Guid> SkillGroupDeleted { get; set; }

    [Parameter] public EventCallback<string> ErrorOccurred { get; set; }

    [Parameter] public EventCallback<CharacterSheetSkill> RollRequested { get; set; }
}