namespace RpgRoller.Tests;

public sealed class ServiceCampaignTests
{
    [Fact]
    public void CampaignAndCharacterOperations_CheckUnauthorizedAndNotFoundCases()
    {
        using var harness = ServiceTestSupport.CreateHarness();
        var service = harness.Service;

        var unauthorizedCampaign = service.CreateCampaign("missing", "Name", "d6");
        Assert.False(unauthorizedCampaign.Succeeded);

        service.Register("gm", "Password123", "GM");
        var gmSession = ServiceTestSupport.GetValue(service.Login("gm", "Password123")).SessionToken;
        var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Name", "d6"));
        var rolemasterCampaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Rolemaster Name", "rolemaster"));

        var invalidRuleset = service.CreateCampaign(gmSession, "Name 2", "unknown");
        Assert.False(invalidRuleset.Succeeded);
        Assert.Equal("rolemaster", rolemasterCampaign.RulesetId);

        var noCampaignCharacter = service.CreateCharacter(gmSession, "Hero", Guid.NewGuid());
        Assert.False(noCampaignCharacter.Succeeded);

        var character = ServiceTestSupport.GetValue(service.CreateCharacter(gmSession, "Hero", campaign.Id));

        var missingCharacterActivate = service.ActivateCharacter(gmSession, Guid.NewGuid());
        Assert.False(missingCharacterActivate.Succeeded);

        var activateSuccess = service.ActivateCharacter(gmSession, character.Id);
        Assert.True(activateSuccess.Succeeded);

        var currentCharacters = service.GetOwnCharacters(gmSession);
        Assert.True(currentCharacters.Succeeded);
        Assert.Single(ServiceTestSupport.GetValue(currentCharacters));

        var missingCampaignGet = service.GetCampaign(gmSession, Guid.NewGuid());
        Assert.False(missingCampaignGet.Succeeded);
    }

    [Fact]
    public void CurrentCampaignCharacters_ReturnsNoActiveCharacterWhenUnset()
    {
        using var harness = ServiceTestSupport.CreateHarness();
        var service = harness.Service;
        service.Register("user", "Password123", "User");
        var sessionToken = ServiceTestSupport.GetValue(service.Login("user", "Password123")).SessionToken;

        var result = service.GetOwnCharacters(sessionToken);
        Assert.True(result.Succeeded);
        Assert.Empty(ServiceTestSupport.GetValue(result));
    }

    [Fact]
    public void GetCampaigns_ReturnsOwnedAndParticipatingCampaigns()
    {
        using var harness = ServiceTestSupport.CreateHarness();
        var service = harness.Service;
        service.Register("gm", "Password123", "GM");
        service.Register("player", "Password123", "Player");
        var gmSession = ServiceTestSupport.GetValue(service.Login("gm", "Password123")).SessionToken;
        var playerSession = ServiceTestSupport.GetValue(service.Login("player", "Password123")).SessionToken;

        var gmCampaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Owned", "d6"));
        _ = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Owned 2", "d6"));
        _ = service.CreateCharacter(playerSession, "Joiner", gmCampaign.Id);

        var playerCampaigns = service.GetCampaigns(playerSession);
        Assert.True(playerCampaigns.Succeeded);
        var campaigns = ServiceTestSupport.GetValue(playerCampaigns);
        Assert.Single(campaigns);
        Assert.Equal(gmCampaign.Id, campaigns[0].Id);
    }

    [Fact]
    public void GetCharacterCampaignOptions_ReturnsAllCampaignsForCharacterAssignment()
    {
        using var harness = ServiceTestSupport.CreateHarness();
        var service = harness.Service;
        service.Register("gm1", "Password123", "GM One");
        service.Register("gm2", "Password123", "GM Two");
        service.Register("player", "Password123", "Player");
        var gmSession = ServiceTestSupport.GetValue(service.Login("gm1", "Password123")).SessionToken;
        var gmTwoSession = ServiceTestSupport.GetValue(service.Login("gm2", "Password123")).SessionToken;
        var playerSession = ServiceTestSupport.GetValue(service.Login("player", "Password123")).SessionToken;

        var firstCampaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Alpha", "d6"));
        var secondCampaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmTwoSession, "Beta", "d6"));

        var visibleCampaigns = ServiceTestSupport.GetValue(service.GetCampaigns(playerSession));
        Assert.Empty(visibleCampaigns);

        var options = ServiceTestSupport.GetValue(service.GetCharacterCampaignOptions(playerSession));
        Assert.Equal(2, options.Count);
        Assert.Contains(options, option => option.Id == firstCampaign.Id);
        Assert.Contains(options, option => option.Id == secondCampaign.Id);
    }

    [Fact]
    public void GetCampaignAndCharacterSheet_ForNonGmParticipant_ReturnCampaignRosterAndSheet()
    {
        using var harness = ServiceTestSupport.CreateHarness();
        var service = harness.Service;

        service.Register("gm", "Password123", "GM");
        service.Register("owner", "Password123", "Owner");
        service.Register("other", "Password123", "Other");

        var gmSession = ServiceTestSupport.GetValue(service.Login("gm", "Password123")).SessionToken;
        var ownerSession = ServiceTestSupport.GetValue(service.Login("owner", "Password123")).SessionToken;
        var otherSession = ServiceTestSupport.GetValue(service.Login("other", "Password123")).SessionToken;

        var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Main", "d6"));
        var ownerCharacter = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Owner Character", campaign.Id));
        var otherCharacter = ServiceTestSupport.GetValue(service.CreateCharacter(otherSession, "Other Character", campaign.Id));

        var ownerSkill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, ownerCharacter.Id, "Stealth", "2D+1", 1, true));
        _ = ServiceTestSupport.GetValue(service.CreateSkill(otherSession, otherCharacter.Id, "Perception", "1D+2", 1, true));

        var ownerView = ServiceTestSupport.GetValue(service.GetCampaign(ownerSession, campaign.Id));
        Assert.Equal(2, ownerView.Characters.Length);
        Assert.Contains(ownerView.Characters, character => character.Id == ownerCharacter.Id);
        Assert.Contains(ownerView.Characters, character => character.Id == otherCharacter.Id);

        var ownerSheet = ServiceTestSupport.GetValue(service.GetCharacterSheet(ownerSession, ownerCharacter.Id));
        var otherSheet = ServiceTestSupport.GetValue(service.GetCharacterSheet(ownerSession, otherCharacter.Id));
        Assert.Single(ownerSheet.Skills);
        Assert.Contains(ownerSheet.Skills, skill => skill.Id == ownerSkill.Id);
        Assert.Single(otherSheet.Skills);
    }

    [Fact]
    public void CampaignStateSnapshot_TracksRosterCharacterAndLogSlicesIndependently()
    {
        using var harness = ServiceTestSupport.CreateHarness(4, 5, 6, 3);
        var service = harness.Service;

        service.Register("gm-state", "Password123", "GM");
        service.Register("owner-state", "Password123", "Owner");

        var gmSession = ServiceTestSupport.GetValue(service.Login("gm-state", "Password123")).SessionToken;
        var ownerSession = ServiceTestSupport.GetValue(service.Login("owner-state", "Password123")).SessionToken;

        var campaign = ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "State Campaign", "d6"));
        var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "State Hero", campaign.Id));
        var afterCharacterCreate = ServiceTestSupport.GetValue(service.GetCampaignStateSnapshot(ownerSession, campaign.Id));

        var initialCharacterVersion = Assert.Single(afterCharacterCreate.CharacterVersions, version => version.CharacterId == character.Id).Version;
        Assert.True(afterCharacterCreate.RosterVersion > 1);

        var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, character.Id, "Stealth", "2D+1", 1, true));
        var afterSkillCreate = ServiceTestSupport.GetValue(service.GetCampaignStateSnapshot(ownerSession, campaign.Id));
        var updatedCharacterVersion = Assert.Single(afterSkillCreate.CharacterVersions, version => version.CharacterId == character.Id).Version;

        Assert.Equal(afterCharacterCreate.RosterVersion, afterSkillCreate.RosterVersion);
        Assert.True(updatedCharacterVersion > initialCharacterVersion);

        _ = ServiceTestSupport.GetValue(service.RollSkill(ownerSession, skill.Id, "public"));
        var afterRoll = ServiceTestSupport.GetValue(service.GetCampaignStateSnapshot(ownerSession, campaign.Id));

        Assert.Equal(afterSkillCreate.RosterVersion, afterRoll.RosterVersion);
        Assert.Equal(updatedCharacterVersion, Assert.Single(afterRoll.CharacterVersions, version => version.CharacterId == character.Id).Version);
        Assert.True(afterRoll.LogVersion > afterSkillCreate.LogVersion);
    }
}