using System.Net;
using System.Net.Http.Json;
using GameList.Tests.Support;
using Microsoft.EntityFrameworkCore;

namespace GameList.Tests;

public class VoteTests
{
    [Fact]
    public async Task Finalizing_votes_blocks_further_changes()
    {
        using var factory = new TestWebApplicationFactory();
        var client = factory.CreateClientWithCookies();
        await client.RegisterAsync("voter");

        var suggestionId = await client.CreateSuggestionAsync("VoteGame");

        await client.PostAsJsonAsync("/api/me/phase/next", new { });

        var vote = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = suggestionId, Score = 7 });
        vote.EnsureSuccessStatusCode();

        var finalize = await client.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
        finalize.EnsureSuccessStatusCode();

        var change = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = suggestionId, Score = 5 });

        Assert.Equal(HttpStatusCode.BadRequest, change.StatusCode);
    }

    [Fact]
    public async Task Score_out_of_range_rejected()
    {
        using var factory = new TestWebApplicationFactory();
        var client = factory.CreateClientWithCookies();
        await client.RegisterAsync("score");
        var id = await client.CreateSuggestionAsync("RangeGame");
        await client.PostAsJsonAsync("/api/me/phase/next", new { });

        var resp = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = id, Score = 11 });
        Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
    }

    [Fact]
    public async Task Invalid_suggestion_id_rejected()
    {
        using var factory = new TestWebApplicationFactory();
        var client = factory.CreateClientWithCookies();
        await client.RegisterAsync("invalid");
        await client.PostAsJsonAsync("/api/me/phase/next", new { });

        var resp = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = 9999, Score = 5 });
        Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
    }

    [Fact]
    public async Task Votes_require_display_name()
    {
        using var factory = new TestWebApplicationFactory();
        var client = factory.CreateClientWithCookies();
        await client.RegisterAsync("anon");
        var id = await client.CreateSuggestionAsync("NeedName");

        await factory.WithDbContextAsync(async db =>
        {
            var p = await db.Players.FirstAsync();
            p.DisplayName = null;
            await db.SaveChangesAsync();
        });

        await client.PostAsJsonAsync("/api/me/phase/next", new { });
        var resp = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = id, Score = 5 });
        Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
    }

    [Fact]
    public async Task Finalize_only_in_vote_phase()
    {
        using var factory = new TestWebApplicationFactory();
        var client = factory.CreateClientWithCookies();
        await client.RegisterAsync("phase");

        var resp = await client.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
        Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
    }

    [Fact]
    public async Task Linked_votes_apply_to_all_linked_suggestions()
    {
        using var factory = new TestWebApplicationFactory();
        var admin = factory.CreateClientWithCookies();
        await admin.RegisterAsync("admin", admin: true);
        await admin.PostAsJsonAsync("/api/me/phase/next", new { });

        var player = factory.CreateClientWithCookies();
        await player.RegisterAsync("linker");

        var id1 = await player.CreateSuggestionAsync("Game A");
        var id2 = await player.CreateSuggestionAsync("Game B");

        await player.PostAsJsonAsync("/api/me/phase/next", new { });

        var linkResponse = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = id1, TargetSuggestionId = id2 });
        linkResponse.EnsureSuccessStatusCode();

        var vote = await player.PostAsJsonAsync("/api/votes", new { SuggestionId = id1, Score = 9 });
        vote.EnsureSuccessStatusCode();

        var mine = await player.GetFromJsonAsync<List<VoteRecord>>("/api/votes/mine");
        Assert.NotNull(mine);

        Assert.Equal(2, mine!.Count);
        Assert.All(mine, v => Assert.Equal(9, v.Score));
    }

    private record VoteRecord(int SuggestionId, int Score);
}
