using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Api.Contracts;
using Tiku.Application.Assets;
using Tiku.Application.Catalog;
using Tiku.Domain.Common;
using Tiku.Domain.Content;
using Tiku.Domain.Identity;
using Tiku.Domain.QuestionBanks;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Auth;
using Tiku.Infrastructure.Persistence;

namespace Tiku.IntegrationTests.Api;

public sealed class VideoEndpointTests
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        Converters = { new JsonStringEnumConverter() }
    };

    [Fact]
    public async Task Student_can_search_play_report_progress_and_query_question_videos()
    {
        await using var factory = new ApiTestFactory();
        var seed = await SeedStudentAsync(factory);
        var questionId = Guid.NewGuid();
        var videoId = Guid.NewGuid();
        await factory.SeedAsync(
            new Question { Id = questionId, TenantId = seed.TenantId, Type = "choice", Status = QuestionStatus.Published },
            new VideoExplanation
            {
                Id = videoId,
                TenantId = seed.TenantId,
                Title = "透视解析",
                Description = "素描透视",
                VideoUrl = "https://cdn.example.test/video.mp4",
                DurationSeconds = 120,
                IsActive = true
            },
            new QuestionVideo
            {
                TenantId = seed.TenantId,
                QuestionId = questionId,
                VideoId = videoId
            });
        using var client = factory.CreateClient();
        await LoginAsync(client, seed);

        var search = await client.GetFromJsonAsync<CatalogList<VideoExplanationCatalogItem>>("/api/videos/search?keyword=透视", JsonOptions);
        var questionVideos = await client.GetFromJsonAsync<CatalogList<QuestionVideoCatalogItem>>($"/api/questions/videos?questionId={questionId}", JsonOptions);
        var batch = await (await client.PostAsJsonAsync(
                "/api/questions/videos/batch",
                new QuestionVideoQueryDto { QuestionIds = [questionId] }))
            .Content
            .ReadFromJsonAsync<CatalogList<QuestionVideoCatalogItem>>(JsonOptions);
        var playResponse = await client.PostAsJsonAsync(
            "/api/videos/play",
            new VideoPlayDto { VideoId = videoId, QuestionId = questionId });
        var play = await playResponse.Content.ReadFromJsonAsync<VideoPlaybackItem>(JsonOptions);
        var progressResponse = await client.PostAsJsonAsync(
            "/api/videos/progress",
            new VideoProgressDto
            {
                VideoId = videoId,
                QuestionId = questionId,
                PositionSeconds = 120,
                DurationSeconds = 120,
                WatchedSeconds = 120
            });
        var progress = await progressResponse.Content.ReadFromJsonAsync<VideoProgressItem>(JsonOptions);

        Assert.Single(search!.Items);
        Assert.Single(questionVideos!.Items);
        Assert.Single(batch!.Items);
        Assert.Equal(HttpStatusCode.OK, playResponse.StatusCode);
        Assert.Equal("https://cdn.example.test/video.mp4", play!.PlayUrl);
        Assert.Equal(HttpStatusCode.OK, progressResponse.StatusCode);
        Assert.True(progress!.IsCompleted);

        using var scope = factory.CreateSystemScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.Single(dbContext.VideoPlaybackProgress.Where(item =>
            item.TenantId == seed.TenantId &&
            item.UserId == seed.UserId &&
            item.VideoId == videoId &&
            item.QuestionId == questionId));
        Assert.Single(dbContext.ContentAssetAccessEvents.Where(item =>
            item.TenantId == seed.TenantId &&
            item.UserId == seed.UserId &&
            item.AssetType == "Video"));
    }

    [Fact]
    public async Task Student_cannot_play_another_tenant_video()
    {
        await using var factory = new ApiTestFactory();
        var tenantA = await SeedStudentAsync(factory, "13900001001");
        var tenantB = await SeedStudentAsync(factory, "13900001002");
        var videoId = Guid.NewGuid();
        await factory.SeedAsync(new VideoExplanation
        {
            Id = videoId,
            TenantId = tenantA.TenantId,
            Title = "A tenant video",
            VideoUrl = "https://cdn.example.test/a.mp4",
            IsActive = true
        });
        using var client = factory.CreateClient();
        await LoginAsync(client, tenantB);

        var response = await client.PostAsJsonAsync(
            "/api/videos/play",
            new VideoPlayDto { VideoId = videoId });

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

    private static async Task<(Guid TenantId, Guid UserId, string Phone)> SeedStudentAsync(
        ApiTestFactory factory,
        string? phone = null)
    {
        var tenantId = Guid.NewGuid();
        var userId = Guid.NewGuid();
        var resolvedPhone = phone ?? $"135{Random.Shared.Next(10000000, 99999999)}";
        await factory.SeedAsync(
            new Tenant
            {
                Id = tenantId,
                Slug = tenantId.ToString("N"),
                Name = "Video Tenant",
                Status = TenantStatus.Active,
                Metadata = JsonDefaults.Object()
            },
            new User
            {
                Id = userId,
                Phone = resolvedPhone,
                Name = "Video Student"
            }.WithTestPassword(),
            new TenantMembership
            {
                TenantId = tenantId,
                UserId = userId,
                Role = TenantRole.Student,
                Status = MembershipStatus.Active
            });
        return (tenantId, userId, resolvedPhone);
    }

    private static async Task LoginAsync(
        HttpClient client,
        (Guid TenantId, Guid UserId, string Phone) seed)
    {
        client.UseAccessToken(await client.LoginAsTenantAsync(seed.TenantId, seed.Phone));
    }
}
