using System.Text.Json.Serialization;

namespace RpgRoller.Contracts;

public sealed record HealthResponse(string Status);

public sealed record ApiError(string Error, string? Code = null);

public sealed record RegisterRequest(string Username, string Password, string DisplayName);

public sealed record LoginRequest(string Username, string Password);

public sealed record UserSummary(Guid Id, string Username, string DisplayName, IReadOnlyList<string> Roles);

public sealed record MeResponse(UserSummary User, Guid? ActiveCharacterId, Guid? CurrentCampaignId);

public sealed record AdminUserSummary(Guid Id, string Username, string DisplayName, IReadOnlyList<string> Roles);

public sealed record UpdateUserRolesRequest(IReadOnlyList<string> Roles);

public sealed record RulesetDefinition(string Id, string Name, string DiceSyntax);

public sealed record CreateCampaignRequest(string Name, string RulesetId);

public sealed record CampaignGmSummary(Guid Id, string DisplayName);

public sealed record CampaignSummary(Guid Id, string Name, string RulesetId, CampaignGmSummary Gm, int CharacterCount);

public sealed record CampaignRoster(Guid Id, string Name, string RulesetId, CampaignGmSummary Gm, CharacterSummary[] Characters);

public sealed record CampaignOption(Guid Id, string Name);

public sealed record CreateCharacterRequest(string Name, Guid CampaignId);

public sealed record UpdateCharacterRequest(string Name, Guid? CampaignId, string? OwnerUsername = null);

public sealed record CharacterSummary(Guid Id, string Name, Guid OwnerUserId, Guid? CampaignId, string OwnerDisplayName);

public sealed record CreateSkillRequest(string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, Guid? SkillGroupId = null, int? FumbleRange = null, bool RolemasterAutoRetry = false);

public sealed record UpdateSkillRequest(string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, Guid? SkillGroupId = null, int? FumbleRange = null, bool RolemasterAutoRetry = false);

public sealed record SkillGroupSummary(Guid Id, Guid CharacterId, string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, int? FumbleRange);

public sealed record CreateSkillGroupRequest(string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, int? FumbleRange = null);

public sealed record UpdateSkillGroupRequest(string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, int? FumbleRange = null);

public sealed record SkillSummary(Guid Id, Guid CharacterId, Guid? SkillGroupId, string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, int? FumbleRange, bool RolemasterAutoRetry);

public sealed record RollSkillRequest(string Visibility, int SituationalModifier = 0);

public sealed record CustomRollRequest(string Expression, string Visibility);

public static class RollDieKinds
{
    public const string RolemasterStandard = "rolemaster-standard";
    public const string RolemasterOpenEndedInitial = "rolemaster-open-ended-initial";
    public const string RolemasterOpenEndedHigh = "rolemaster-open-ended-high";
    public const string RolemasterOpenEndedLowSubtract = "rolemaster-open-ended-low-subtract";
}

public sealed record RollDieResult
{
    public RollDieResult()
    {
    }

    public RollDieResult(int roll, bool crit, bool fumble, bool wild, bool removed, bool added, int? sequence = null, string? kind = null, int? signedContribution = null, int? attempt = null)
    {
        Roll = roll;
        Crit = crit;
        Fumble = fumble;
        Wild = wild;
        Removed = removed;
        Added = added;
        Sequence = sequence;
        Kind = kind;
        SignedContribution = signedContribution;
        Attempt = attempt;
    }

    public int Roll { get; init; }
    public bool Crit { get; init; }
    public bool Fumble { get; init; }
    public bool Wild { get; init; }
    public bool Removed { get; init; }
    public bool Added { get; init; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? Sequence { get; init; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Kind { get; init; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? SignedContribution { get; init; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? Attempt { get; init; }
}

public sealed record RollResult(Guid RollId, Guid CampaignId, Guid CharacterId, Guid SkillId, Guid RollerUserId, string Visibility, int Result, string Breakdown, IReadOnlyList<RollDieResult> Dice, DateTimeOffset TimestampUtc);

public sealed record CharacterSheetSkillGroup(Guid Id, string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, int? FumbleRange);

public sealed record CharacterSheetSkill(Guid Id, Guid? SkillGroupId, string Name, string DiceRollDefinition, int WildDice, bool AllowFumble, int? FumbleRange, bool RolemasterAutoRetry);

public sealed record CharacterSheet(Guid CharacterId, CharacterSheetSkillGroup[] SkillGroups, CharacterSheetSkill[] Skills);

public sealed record CampaignLogEntry(Guid RollId, Guid CampaignId, Guid CharacterId, string CharacterName, Guid SkillId, string SkillName, Guid RollerUserId, string RollerDisplayName, string Visibility, int Result, string Breakdown, IReadOnlyList<RollDieResult> Dice, DateTimeOffset TimestampUtc);

public sealed record CampaignLogListEntry(
    Guid RollId,
    string CharacterName,
    string SkillName,
    string RollerLabel,
    string VisibilityLabel,
    string VisibilityStyle,
    int Result,
    string SummaryText,
    [property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    string[]? EventBadges,
    DateTimeOffset TimestampUtc);

public sealed record CampaignRollDetail(Guid RollId, string Breakdown, RollDieResult[] Dice);

public sealed record CharacterStateVersion(Guid CharacterId, long Version);

public sealed record CampaignStateSnapshot(Guid CampaignId, long TotalVersion, long RosterVersion, long LogVersion, IReadOnlyList<CharacterStateVersion> CharacterVersions);

public sealed record CampaignLogPage(CampaignLogListEntry[] Entries, Guid? Cursor, bool HasMore, bool ResetRequired);