namespace RpgRoller.Tests;

public sealed class ServicePersistenceTests
{
    [Fact]
    public void ServiceGuardAndPersistenceBranches_AreHandled()
    {
        using var harness = ServiceTestSupport.CreateHarness(2, 3, 4);
        var service = harness.Service;

        var invalidCredentials = service.Login("", "");
        Assert.False(invalidCredentials.Succeeded);

        service.Logout("missing-session");

        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));

        Assert.False(service.GetMe(string.Empty).Succeeded);
        Assert.False(service.CreateCampaign(gmSession, "", "d6").Succeeded);
        Assert.False(service.GetCampaigns(string.Empty).Succeeded);
        Assert.False(service.CreateCharacter(ownerSession, "", campaign.Id).Succeeded);
        Assert.False(service.CreateCharacter(string.Empty, "Name", campaign.Id).Succeeded);
        Assert.False(service.UpdateCharacter(string.Empty, ownerCharacter.Id, "Renamed", campaign.Id).Succeeded);
        Assert.False(service.UpdateCharacter(ownerSession, Guid.NewGuid(), "Renamed", campaign.Id).Succeeded);
        Assert.False(service.ActivateCharacter(string.Empty, ownerCharacter.Id).Succeeded);
        Assert.True(service.ActivateCharacter(gmSession, ownerCharacter.Id).Succeeded);
        Assert.False(service.GetOwnCharacters(string.Empty).Succeeded);
        Assert.False(service.CreateSkill(string.Empty, ownerCharacter.Id, "Stealth", "2D+1", 1, true).Succeeded);
        Assert.False(service.CreateSkill(ownerSession, Guid.NewGuid(), "Stealth", "2D+1", 1, true).Succeeded);
        Assert.False(service.CreateSkill(otherSession, ownerCharacter.Id, "Stealth", "2D+1", 1, true).Succeeded);

        var gmMe = ServiceTestSupport.GetValue(service.GetMe(gmSession));
        Assert.Equal(ownerCharacter.Id, gmMe.ActiveCharacterId);
        Assert.Equal(campaign.Id, gmMe.CurrentCampaignId);

        using (var db = harness.CreateDbContext())
        {
            var ownerUser = db.Users.Single(u => u.UsernameNormalized == "OWNER");
            ownerUser.ActiveCharacterId = Guid.NewGuid();
            db.SaveChanges();
        }

        using var staleMeHarness = ServiceTestSupport.CreateHarnessFromPath(harness.DbPath, 2, 3, 4);
        var staleMeService = staleMeHarness.Service;

        var staleMe = ServiceTestSupport.GetValue(staleMeService.GetMe(ownerSession));
        Assert.Null(staleMe.ActiveCharacterId);
        Assert.Null(staleMe.CurrentCampaignId);

        Assert.True(staleMeService.ActivateCharacter(ownerSession, ownerCharacter.Id).Succeeded);
        var activeMe = ServiceTestSupport.GetValue(staleMeService.GetMe(ownerSession));
        Assert.Equal(ownerCharacter.Id, activeMe.ActiveCharacterId);
        Assert.Equal(campaign.Id, activeMe.CurrentCampaignId);

        using (var db = harness.CreateDbContext())
        {
            var staleOwner = db.Users.Single(u => u.UsernameNormalized == "OWNER");
            staleOwner.ActiveCharacterId = Guid.NewGuid();
            db.SaveChanges();
        }

        using var staleCurrentHarness = ServiceTestSupport.CreateHarnessFromPath(harness.DbPath, 2, 3, 4);
        var staleCurrentService = staleCurrentHarness.Service;

        var staleCurrentCampaign = staleCurrentService.GetOwnCharacters(ownerSession);
        Assert.True(staleCurrentCampaign.Succeeded);
        Assert.Single(ServiceTestSupport.GetValue(staleCurrentCampaign));
        using (var db = harness.CreateDbContext())
        {
            Assert.NotNull(db.Users.Single(u => u.UsernameNormalized == "OWNER").ActiveCharacterId);
        }

        var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, ownerCharacter.Id, "Stealth", "2D+1",
            1, true));
        Assert.False(service.UpdateSkill(ownerSession, skill.Id, "", "2D+1", 1, true).Succeeded);
        Assert.False(service.UpdateSkill(string.Empty, skill.Id, "Stealth", "2D+1", 1, true).Succeeded);
        Assert.False(service.UpdateSkill(ownerSession, skill.Id, "Stealth", "bad", 1, true).Succeeded);
        Assert.False(service.RollSkill(string.Empty, skill.Id, "public").Succeeded);

        using (var db = harness.CreateDbContext())
        {
            var mutableSkill = db.Skills.Single(s => s.Id == skill.Id);
            mutableSkill.DiceRollDefinition = "bad";
            db.SaveChanges();
        }

        using var invalidExpressionHarness = ServiceTestSupport.CreateHarnessFromPath(harness.DbPath, 2, 3, 4);
        Assert.False(invalidExpressionHarness.Service.RollSkill(ownerSession, skill.Id, "public").Succeeded);
        Assert.False(service.GetCampaignLog(string.Empty, campaign.Id).Succeeded);
    }

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

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

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

        var campaign =
            ServiceTestSupport.GetValue(service.CreateCampaign(gmSession, "Rolemaster Persistence", "rolemaster"));
        var character = ServiceTestSupport.GetValue(service.CreateCharacter(ownerSession, "Loremaster", campaign.Id));
        var group = ServiceTestSupport.GetValue(service.CreateSkillGroup(ownerSession, character.Id, "Perception",
            "d100!+25", 0, false, 5));
        var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, character.Id, "Read Runes",
            "d100!+35", 0, false, group.Id, 3, true));

        using var reloadedHarness = ServiceTestSupport.CreateHarnessFromPath(harness.DbPath);
        var reloadedSheet =
            ServiceTestSupport.GetValue(reloadedHarness.Service.GetCharacterSheet(ownerSession, character.Id));

        var reloadedGroup = Assert.Single(reloadedSheet.SkillGroups, current => current.Id == group.Id);
        Assert.Equal(5, reloadedGroup.FumbleRange);

        var reloadedSkill = Assert.Single(reloadedSheet.Skills, current => current.Id == skill.Id);
        Assert.Equal(3, reloadedSkill.FumbleRange);
        Assert.True(reloadedSkill.RolemasterAutoRetry);
    }
}