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

namespace GameList.Tests;

public class SuggestionTests
{
    [Fact]
    public async Task Player_cannot_exceed_five_suggestions()
    {
        using var factory = new TestWebApplicationFactory();
        var client = factory.CreateClientWithCookies();
        await client.RegisterAsync("suggestor");

        for (var i = 0; i < 5; i++)
        {
            var resp = await client.PostAsJsonAsync("/api/suggestions", new
            {
                Name = $"Game {i}",
                Genre = (string?)null,
                Description = (string?)null,
                ScreenshotUrl = (string?)null,
                YoutubeUrl = (string?)null,
                GameUrl = (string?)null,
                MinPlayers = (int?)null,
                MaxPlayers = (int?)null
            });
            resp.EnsureSuccessStatusCode();
        }

        var sixth = await client.PostAsJsonAsync("/api/suggestions", new
        {
            Name = "Overflow",
            Genre = (string?)null,
            Description = (string?)null,
            ScreenshotUrl = (string?)null,
            YoutubeUrl = (string?)null,
            GameUrl = (string?)null,
            MinPlayers = (int?)null,
            MaxPlayers = (int?)null
        });

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

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

        var badExt = await client.PostAsJsonAsync("/api/suggestions", new
        {
            Name = "BadImg",
            Genre = (string?)null,
            Description = (string?)null,
            ScreenshotUrl = "http://example.com/file.txt",
            YoutubeUrl = (string?)null,
            GameUrl = (string?)null,
            MinPlayers = (int?)null,
            MaxPlayers = (int?)null
        });
        Assert.Equal(HttpStatusCode.BadRequest, badExt.StatusCode);

        var badPlayers = await client.PostAsJsonAsync("/api/suggestions", new
        {
            Name = "BadPlayers",
            Genre = (string?)null,
            Description = (string?)null,
            ScreenshotUrl = (string?)null,
            YoutubeUrl = (string?)null,
            GameUrl = (string?)null,
            MinPlayers = 4,
            MaxPlayers = 2
        });
        Assert.Equal(HttpStatusCode.BadRequest, badPlayers.StatusCode);
    }

    [Fact]
    public async Task Joker_allows_single_extra_suggestion_and_unfinalizes_votes()
    {
        using var factory = new TestWebApplicationFactory();
        var player = factory.CreateClientWithCookies();
        await player.RegisterAsync("joker");

        await factory.WithDbContextAsync(async db =>
        {
            var p = await db.Players.FirstAsync();
            p.HasJoker = true;
            p.CurrentPhase = Domain.Phase.Vote;
            await db.SaveChangesAsync();
        });

        var suggestion = await player.PostAsJsonAsync("/api/suggestions", new
        {
            Name = "JokerGame",
            Genre = (string?)null,
            Description = (string?)null,
            ScreenshotUrl = (string?)null,
            YoutubeUrl = (string?)null,
            GameUrl = (string?)null,
            MinPlayers = (int?)null,
            MaxPlayers = (int?)null
        });
        suggestion.EnsureSuccessStatusCode();

        await factory.WithDbContextAsync(async db =>
        {
            var p = await db.Players.FirstAsync();
            Assert.False(p.HasJoker);
            Assert.False(p.VotesFinal);
        });
    }

    [Fact]
    public async Task Admin_can_update_during_vote_phase()
    {
        using var factory = new TestWebApplicationFactory();
        var admin = factory.CreateClientWithCookies();
        await admin.RegisterAsync("admin", admin: true);

        var player = factory.CreateClientWithCookies();
        await player.RegisterAsync("author");
        var id = await player.CreateSuggestionAsync("OldName");
        await player.PostAsJsonAsync("/api/me/phase/next", new { });

        var update = await admin.PutAsJsonAsync($"/api/suggestions/{id}", new
        {
            Name = "NewName",
            Genre = (string?)null,
            Description = (string?)null,
            ScreenshotUrl = (string?)null,
            YoutubeUrl = (string?)null,
            GameUrl = (string?)null,
            MinPlayers = (int?)null,
            MaxPlayers = (int?)null
        });
        update.EnsureSuccessStatusCode();
    }

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

        var update = await player.PutAsJsonAsync($"/api/suggestions/{id}", new
        {
            Name = "Blocked",
            Genre = "NewGenre",
            Description = (string?)null,
            ScreenshotUrl = (string?)null,
            YoutubeUrl = (string?)null,
            GameUrl = (string?)null,
            MinPlayers = (int?)null,
            MaxPlayers = (int?)null
        });

        update.EnsureSuccessStatusCode();

        var loaded = await factory.WithDbContextAsync(async db => await db.Suggestions.FindAsync(id));
        Assert.Equal("Lock", loaded!.Name); // title locked
    }

    [Fact]
    public async Task Unreachable_screenshot_url_is_rejected()
    {
        using var factory = new TestWebApplicationFactory();
        factory.HttpHandler.SetResponder(_ => new HttpResponseMessage(HttpStatusCode.BadRequest));

        var client = factory.CreateClientWithCookies();
        await client.RegisterAsync("imgtester");

        var response = await client.PostAsJsonAsync("/api/suggestions", new
        {
            Name = "Needs image",
            Genre = (string?)null,
            Description = (string?)null,
            ScreenshotUrl = "http://example.com/image.png",
            YoutubeUrl = (string?)null,
            GameUrl = (string?)null,
            MinPlayers = (int?)null,
            MaxPlayers = (int?)null
        });

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

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

        var resp = await client.GetAsync("/api/suggestions/all");
        Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
    }

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

        await client.PostAsJsonAsync("/api/suggestions", new { Name = "Second", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = (int?)null, MaxPlayers = (int?)null });
        await Task.Delay(10);
        await client.PostAsJsonAsync("/api/suggestions", new { Name = "Third", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = (int?)null, MaxPlayers = (int?)null });

        var mine = await client.GetFromJsonAsync<List<JsonElement>>("/api/suggestions/mine");
        Assert.Equal("Second", mine![0].GetProperty("name").GetString());
    }
}
