using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SVSim.Database;
using SVSim.Database.Enums;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.Admin;
using SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Admin;
using SVSim.UnitTests.Infrastructure;

namespace SVSim.UnitTests.Controllers;

/// <summary>
/// End-to-end coverage for <c>/admin/import_viewer</c>. The endpoint is [AllowAnonymous] so
/// these tests don't need to seed a viewer first; the fresh-user path exercises the just-fixed
/// nav-graph NRE inside <c>ViewerRepository.RegisterViewer</c>, and the existing-user path
/// exercises the owned-type lookup used to dedupe by Steam id.
/// </summary>
public class AdminControllerTests
{
    private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };

    [Test]
    public async Task ImportViewer_fresh_user_creates_viewer_and_returns_ids()
    {
        using var factory = new SVSimTestFactory();
        using var client = factory.CreateClient();

        var response = await client.PostAsJsonAsync("/admin/import_viewer", new ImportViewerRequest
        {
            SteamId = 76_561_198_222_333_444UL,
            DisplayName = "Fresh User",
            CountryCode = "USA",
            TutorialState = 100,
            Currency = new ImportCurrency { Crystals = 12345 }
        });

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK),
            await response.Content.ReadAsStringAsync());

        var body = await response.Content.ReadFromJsonAsync<ImportViewerResponse>(JsonOptions);

        Assert.That(body, Is.Not.Null);
        Assert.That(body!.ViewerId, Is.GreaterThan(0), "RegisterViewer must persist and return a non-zero id.");
        Assert.That(body.WasCreated, Is.True);

        using var scope = factory.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
        var stored = await db.Viewers
            .Include(v => v.SocialAccountConnections)
            .Include(v => v.Currency)
            .Include(v => v.Info)
            .FirstAsync(v => v.Id == body.ViewerId);

        Assert.That(stored.DisplayName, Is.EqualTo("Fresh User"));
        Assert.That(stored.Currency.Crystals, Is.EqualTo(12345UL),
            "ImportViewer should overwrite the seed-config crystal default with the requested value.");
        Assert.That(stored.Info.CountryCode, Is.EqualTo("USA"));
        Assert.That(stored.SocialAccountConnections.Count, Is.EqualTo(1));
        Assert.That(stored.SocialAccountConnections[0].AccountId, Is.EqualTo(76_561_198_222_333_444UL));
        Assert.That(stored.SocialAccountConnections[0].AccountType, Is.EqualTo(SocialAccountType.Steam));
    }

    [Test]
    public async Task ImportViewer_existing_user_updates_in_place()
    {
        using var factory = new SVSimTestFactory();
        const ulong steamId = 76_561_198_555_666_777UL;
        long seededId = await factory.SeedViewerAsync(steamId: steamId, displayName: "Original Name");

        using var client = factory.CreateClient();
        var response = await client.PostAsJsonAsync("/admin/import_viewer", new ImportViewerRequest
        {
            SteamId = steamId,
            DisplayName = "Updated Name",
            CountryCode = "JPN"
        });

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK),
            await response.Content.ReadAsStringAsync());

        var body = await response.Content.ReadFromJsonAsync<ImportViewerResponse>(JsonOptions);

        Assert.That(body, Is.Not.Null);
        Assert.That(body!.ViewerId, Is.EqualTo(seededId),
            "Re-importing the same SteamId must reuse the existing viewer row, not create a new one.");
        Assert.That(body.WasCreated, Is.False);

        using var scope = factory.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
        var stored = await db.Viewers
            .Include(v => v.Info)
            .FirstAsync(v => v.Id == seededId);

        Assert.That(stored.DisplayName, Is.EqualTo("Updated Name"));
        Assert.That(stored.Info.CountryCode, Is.EqualTo("JPN"));

        var viewerCount = await db.Viewers.CountAsync(v =>
            v.SocialAccountConnections.Any(s => s.AccountType == SocialAccountType.Steam && s.AccountId == steamId));
        Assert.That(viewerCount, Is.EqualTo(1), "Owned-type dedup must not produce a second row.");
    }

    [Test]
    public async Task ImportViewer_missing_steam_id_returns_400()
    {
        using var factory = new SVSimTestFactory();
        using var client = factory.CreateClient();

        var response = await client.PostAsJsonAsync("/admin/import_viewer", new ImportViewerRequest
        {
            SteamId = 0,
            DisplayName = "No Steam"
        });

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
    }
}
