using DodoSSH.Contracts;
using DodoSSH.Domain;
using DodoSSH.Domain.Authorization;

namespace DodoSSH.Api.Tests;

/// <summary>
/// The team enums on the wire and the ones in the domain have to agree, and nothing but this makes them.
/// </summary>
/// <remarks>
/// <para>
/// The same hazard <c>EntityTypeAlignmentTests</c> exists for, one feature along and with a worse failure.
/// <c>TeamService</c> maps <see cref="TeamMemberRole"/> to <see cref="TeamRole"/> member by member, so a
/// renumbering on one side does not fail to compile — it silently changes what a role means. Someone
/// added as a viewer would come back as an admin, or the reverse, on the next deployment.
/// </para>
/// <para>
/// Mapped by <em>name</em> in the service and asserted by <em>value</em> here, which is the pairing that
/// catches the mistake: the service would go on compiling after a renumbering, and this would not go on
/// passing.
/// </para>
/// </remarks>
public sealed class TeamEnumAlignmentTests
{
    [Fact]
    public void EveryWireRole_HasADomainRoleWithTheSameValue()
    {
        ((int)TeamMemberRole.Unspecified).ShouldBe((int)TeamRole.Unspecified);
        ((int)TeamMemberRole.Viewer).ShouldBe((int)TeamRole.Viewer);
        ((int)TeamMemberRole.Member).ShouldBe((int)TeamRole.Member);
        ((int)TeamMemberRole.Admin).ShouldBe((int)TeamRole.Admin);
        ((int)TeamMemberRole.Owner).ShouldBe((int)TeamRole.Owner);
    }

    [Fact]
    public void TheTwoRoleEnums_HaveTheSameNumberOfMembers() =>
        Enum.GetValues<TeamMemberRole>().Length.ShouldBe(Enum.GetValues<TeamRole>().Length);

    [Fact]
    public void EveryWireMembershipStatus_HasADomainStatusWithTheSameValue()
    {
        ((int)TeamMemberStatus.Unspecified).ShouldBe((int)MembershipStatus.Unspecified);
        ((int)TeamMemberStatus.Invited).ShouldBe((int)MembershipStatus.Invited);
        ((int)TeamMemberStatus.Active).ShouldBe((int)MembershipStatus.Active);
        ((int)TeamMemberStatus.Revoked).ShouldBe((int)MembershipStatus.Revoked);
    }

    [Fact]
    public void EveryWireGrantState_HasADomainStateWithTheSameValue()
    {
        ((int)VaultGrantState.Unspecified).ShouldBe((int)GrantState.Unspecified);
        ((int)VaultGrantState.Active).ShouldBe((int)GrantState.Active);
        ((int)VaultGrantState.AwaitingRewrap).ShouldBe((int)GrantState.AwaitingRewrap);
        ((int)VaultGrantState.Revoked).ShouldBe((int)GrantState.Revoked);
    }

    /// <remarks>
    /// <para>
    /// <c>VaultSummary.Permissions</c> is an opaque int on the wire, on purpose — the flags live in
    /// <c>DodoSSH.Domain</c> and no client project references that assembly. So the client repeats the one
    /// bit it needs as a literal in <c>StoredVault.CanWrite</c>, and this pins the value it copied.
    /// </para>
    /// <para>
    /// Asserted here rather than against <c>StoredVault</c> itself, which would mean a server test project
    /// taking a reference on a client assembly to check a constant. The consequence of drift is worth the
    /// literal either way: a Save button offered to somebody who is only a viewer of a team vault, ending
    /// in a 403 they can do nothing about — or no Save button for somebody who may write.
    /// </para>
    /// </remarks>
    [Fact]
    public void TheWriteFlag_IsTheBitTheClientCopied()
    {
        ((int)PermissionFlags.Read).ShouldBe(1 << 0);
        ((int)PermissionFlags.Write).ShouldBe(1 << 1);
    }
}
