using RpgRoller.Contracts;
using RpgRoller.Domain;

namespace RpgRoller.Services;

public static class GameDtoMapper
{
    public static UserSummary ToUserSummary(UserAccount user)
    {
        return new(user.Id, user.Username, user.DisplayName, RoleSerializer.Parse(user.Roles));
    }

    public static AdminUserSummary ToAdminUserSummary(UserAccount user)
    {
        return new(user.Id, user.Username, user.DisplayName, RoleSerializer.Parse(user.Roles));
    }

    public static CampaignOption ToCampaignOption(Campaign campaign)
    {
        return new(campaign.Id, campaign.Name);
    }

    public static CampaignSummary ToCampaignSummary(GameStateStore stateStore, Campaign campaign)
    {
        var gm = stateStore.UsersById[campaign.GmUserId];
        var characterCount = stateStore.CharactersById.Values.Count(character => character.CampaignId == campaign.Id);
        return new(campaign.Id, campaign.Name, DiceRules.ToRulesetId(campaign.Ruleset), new(gm.Id, gm.DisplayName), characterCount);
    }

    public static CampaignRoster ToCampaignRoster(GameStateStore stateStore, Campaign campaign)
    {
        var gm = stateStore.UsersById[campaign.GmUserId];
        var characters = stateStore.CharactersById.Values.Where(character => character.CampaignId == campaign.Id).OrderBy(character => character.Name, StringComparer.OrdinalIgnoreCase).Select(character => ToCharacterSummary(stateStore, character)).ToArray();

        return new(campaign.Id, campaign.Name, DiceRules.ToRulesetId(campaign.Ruleset), new(gm.Id, gm.DisplayName), characters);
    }

    public static CharacterSummary ToCharacterSummary(GameStateStore stateStore, Character character)
    {
        return new(character.Id, character.Name, character.OwnerUserId, character.CampaignId, ResolveOwnerDisplayName(stateStore, character.OwnerUserId, "Unknown user"));
    }

    public static CharacterSheet ToCharacterSheet(GameStateStore stateStore, Guid characterId)
    {
        var skillGroups = stateStore.SkillGroupsById.Values.Where(group => group.CharacterId == characterId).OrderBy(group => group.Name, StringComparer.OrdinalIgnoreCase).Select(ToCharacterSheetSkillGroup).ToArray();
        var skills = stateStore.SkillsById.Values.Where(skill => skill.CharacterId == characterId).OrderBy(skill => skill.Name, StringComparer.OrdinalIgnoreCase).Select(ToCharacterSheetSkill).ToArray();

        return new(characterId, skillGroups, skills);
    }

    public static SkillGroupSummary ToSkillGroupSummary(SkillGroup skillGroup)
    {
        return new(skillGroup.Id, skillGroup.CharacterId, skillGroup.Name, skillGroup.DiceRollDefinition, skillGroup.WildDice, skillGroup.AllowFumble, skillGroup.FumbleRange);
    }

    public static SkillSummary ToSkillSummary(Skill skill)
    {
        return new(skill.Id, skill.CharacterId, skill.SkillGroupId, skill.Name, skill.DiceRollDefinition, skill.WildDice, skill.AllowFumble, skill.FumbleRange, skill.RolemasterAutoRetry);
    }

    public static RollResult ToRollResult(RollLogEntry entry, IReadOnlyList<RollDieResult> dice)
    {
        return new(entry.Id, entry.CampaignId, entry.CharacterId, entry.SkillId, entry.RollerUserId, entry.Visibility == RollVisibility.Public ? "public" : "private", entry.Result, entry.Breakdown, dice, entry.TimestampUtc);
    }

    public static CampaignLogEntry ToCampaignLogEntry(RollLogEntry entry, string characterName, string skillName, string rollerDisplayName, IReadOnlyList<RollDieResult> dice)
    {
        return new(entry.Id, entry.CampaignId, entry.CharacterId, characterName, entry.SkillId, skillName, entry.RollerUserId, rollerDisplayName, entry.Visibility == RollVisibility.Public ? "public" : "private", entry.Result, entry.Breakdown, dice, entry.TimestampUtc);
    }

    public static CampaignLogListEntry ToCampaignLogListEntry(RollLogEntry entry, string characterName, string skillName, string rollerLabel, string visibilityLabel, string visibilityStyle, string summaryText, string[]? eventBadges)
    {
        return new(entry.Id, characterName, skillName, rollerLabel, visibilityLabel, visibilityStyle, entry.Result, summaryText, eventBadges, entry.TimestampUtc);
    }

    public static CampaignRollDetail ToCampaignRollDetail(RollLogEntry entry, RollDieResult[] dice)
    {
        return new(entry.Id, entry.Breakdown, dice);
    }

    public static CampaignStateSnapshot ToCampaignStateSnapshot(GameStateStore stateStore, Guid campaignId)
    {
        var state = stateStore.GetOrCreateCampaignStateLocked(campaignId);
        var characterVersions = state.CharacterVersions.OrderBy(version => version.Key).Select(version => new CharacterStateVersion(version.Key, version.Value)).ToArray();

        return new(campaignId, state.TotalVersion, state.RosterVersion, state.LogVersion, characterVersions);
    }

    public static string ResolveOwnerDisplayName(GameStateStore stateStore, Guid ownerUserId, string fallback)
    {
        return stateStore.UsersById.TryGetValue(ownerUserId, out var user) && !string.IsNullOrWhiteSpace(user.DisplayName) ? user.DisplayName : fallback;
    }

    private static CharacterSheetSkillGroup ToCharacterSheetSkillGroup(SkillGroup skillGroup)
    {
        return new(skillGroup.Id, skillGroup.Name, skillGroup.DiceRollDefinition, skillGroup.WildDice, skillGroup.AllowFumble, skillGroup.FumbleRange);
    }

    private static CharacterSheetSkill ToCharacterSheetSkill(Skill skill)
    {
        return new(skill.Id, skill.SkillGroupId, skill.Name, skill.DiceRollDefinition, skill.WildDice, skill.AllowFumble, skill.FumbleRange, skill.RolemasterAutoRetry);
    }
}