using System.Net;
using System.Net.Http.Json;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using StorageService.Domain.AggregatesModel.FileAggregate;
using StorageService.Domain.AggregatesModel.FileShareAggregate;
using StorageService.Infrastructure.Persistence;

namespace StorageService.FunctionalTests.ApiTests;

/// <summary>
/// EN: Functional tests for File Sharing API endpoints.
/// VI: Functional tests cho File Sharing API endpoints.
/// </summary>
public class FileSharingApiTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory _factory;

    public FileSharingApiTests(CustomWebApplicationFactory factory)
    {
        _factory = factory;
        _client = factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
        
        // EN: Add mock authentication header / VI: Thêm mock authentication header
        _client.DefaultRequestHeaders.Add("X-User-Id", "test-user-123");
    }

    #region CreateShare Tests

    [Fact]
    public async Task CreateShare_ValidRequest_ReturnsShareLink()
    {
        // Arrange
        var fileId = await SeedTestFile("shareable-file.pdf");
        var request = new CreateShareRequest(
            FileId: fileId,
            Permission: "read");

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/storage/shares", request);

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.Created);
        
        if (response.IsSuccessStatusCode)
        {
            var content = await response.Content.ReadFromJsonAsync<CreateShareResponse>();
            content.Should().NotBeNull();
            content!.ShareToken.Should().NotBeNullOrEmpty();
            content.ShareUrl.Should().NotBeNullOrEmpty();
        }
    }

    [Fact]
    public async Task CreateShare_WithPassword_ReturnsProtectedShare()
    {
        // Arrange
        var fileId = await SeedTestFile("protected-file.pdf");
        var request = new CreateShareRequest(
            FileId: fileId,
            Permission: "read",
            Password: "SecretP@ss123");

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/storage/shares", request);

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.Created);
    }

    [Fact]
    public async Task CreateShare_WithExpiration_SetsExpiryDate()
    {
        // Arrange
        var fileId = await SeedTestFile("expiring-file.pdf");
        var expiresAt = DateTime.UtcNow.AddDays(7);
        var request = new CreateShareRequest(
            FileId: fileId,
            Permission: "read",
            ExpiresAt: expiresAt);

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/storage/shares", request);

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.Created);
    }

    [Fact]
    public async Task CreateShare_WithMaxDownloads_SetsLimit()
    {
        // Arrange
        var fileId = await SeedTestFile("limited-file.pdf");
        var request = new CreateShareRequest(
            FileId: fileId,
            Permission: "read",
            MaxDownloads: 5);

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/storage/shares", request);

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.Created);
    }

    [Fact]
    public async Task CreateShare_FileNotFound_Returns404()
    {
        // Arrange
        var request = new CreateShareRequest(
            FileId: Guid.NewGuid(), // Non-existent
            Permission: "read");

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/storage/shares", request);

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.NotFound, HttpStatusCode.BadRequest);
    }

    #endregion

    #region AccessShare Tests

    [Fact]
    public async Task AccessShare_ValidToken_ReturnsFileInfo()
    {
        // Arrange
        var shareToken = await SeedTestShare();

        // Act
        // EN: Public share access - no auth needed
        // VI: Truy cập share công khai - không cần auth
        var unauthClient = _factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
        var response = await unauthClient.GetAsync($"/api/v1/storage/shares/public/{shareToken}");

        // Assert
        // EN: Should return file info or download URL
        // VI: Nên trả về thông tin file hoặc download URL
        response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NotFound);
    }

    [Fact]
    public async Task AccessShare_InvalidToken_Returns404()
    {
        // Arrange
        var invalidToken = "invalid-share-token-12345";

        // Act
        var unauthClient = _factory.CreateClient();
        var response = await unauthClient.GetAsync($"/api/v1/storage/shares/public/{invalidToken}");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NotFound);
    }

    [Fact]
    public async Task AccessShare_ExpiredToken_Returns410()
    {
        // Arrange
        var expiredToken = await SeedTestShare(expired: true);

        // Act
        var unauthClient = _factory.CreateClient();
        var response = await unauthClient.GetAsync($"/api/v1/storage/shares/public/{expiredToken}");

        // Assert
        // EN: 404 or 410 depending on implementation
        // VI: 404 hoặc 410 tùy thuộc vào implementation
        response.StatusCode.Should().BeOneOf(HttpStatusCode.NotFound, HttpStatusCode.Gone);
    }

    #endregion

    #region RevokeShare Tests

    [Fact]
    public async Task RevokeShare_ValidRequest_Returns204()
    {
        // Arrange
        var shareId = await SeedTestShareAndGetId();

        // Act
        var response = await _client.DeleteAsync($"/api/v1/storage/shares/{shareId}");

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.NoContent, HttpStatusCode.OK);
    }

    [Fact]
    public async Task RevokeShare_NotFound_Returns404()
    {
        // Arrange
        var nonExistentId = Guid.NewGuid();

        // Act
        var response = await _client.DeleteAsync($"/api/v1/storage/shares/{nonExistentId}");

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.NotFound, HttpStatusCode.BadRequest);
    }

    #endregion

    #region GetUserShares Tests

    [Fact]
    public async Task GetUserShares_WithShares_ReturnsList()
    {
        // Arrange
        await SeedTestShareAndGetId();
        await SeedTestShareAndGetId();

        // Act
        var response = await _client.GetAsync("/api/v1/storage/shares");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
    }

    #endregion

    #region Helper Methods

    private async Task<Guid> SeedTestFile(string fileName)
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<StorageServiceContext>();

        var file = new StorageFile(
            fileName: fileName,
            bucketName: "test-bucket",
            objectKey: $"private/test-user-123/{DateTime.UtcNow:yyyyMMdd}/{Guid.NewGuid():N}_{fileName}",
            contentType: "application/pdf",
            fileSizeBytes: 1024,
            userId: "test-user-123",
            provider: StorageProvider.MinIO,
            accessLevel: FileAccessLevel.Private);

        context.Files.Add(file);
        await context.SaveChangesAsync();

        return file.Id;
    }

    private async Task<string> SeedTestShare(bool expired = false)
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<StorageServiceContext>();

        // First create a file
        var file = new StorageFile(
            fileName: "share-test.pdf",
            bucketName: "test-bucket",
            objectKey: $"private/test-user-123/{DateTime.UtcNow:yyyyMMdd}/{Guid.NewGuid():N}_share-test.pdf",
            contentType: "application/pdf",
            fileSizeBytes: 1024,
            userId: "test-user-123",
            provider: StorageProvider.MinIO,
            accessLevel: FileAccessLevel.Private);

        context.Files.Add(file);
        await context.SaveChangesAsync();

        // Create share
        var expiresAt = expired ? DateTime.UtcNow.AddDays(-1) : DateTime.UtcNow.AddDays(7);
        var share = new FileShare(
            fileId: file.Id,
            sharedBy: "test-user-123",
            permission: SharePermission.Read,
            expiresAt: expiresAt);

        context.FileShares.Add(share);
        await context.SaveChangesAsync();

        return share.ShareToken;
    }

    private async Task<Guid> SeedTestShareAndGetId()
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<StorageServiceContext>();

        // First create a file
        var file = new StorageFile(
            fileName: "share-test.pdf",
            bucketName: "test-bucket",
            objectKey: $"private/test-user-123/{DateTime.UtcNow:yyyyMMdd}/{Guid.NewGuid():N}_share-test.pdf",
            contentType: "application/pdf",
            fileSizeBytes: 1024,
            userId: "test-user-123",
            provider: StorageProvider.MinIO,
            accessLevel: FileAccessLevel.Private);

        context.Files.Add(file);
        await context.SaveChangesAsync();

        // Create share
        var share = new FileShare(
            fileId: file.Id,
            sharedBy: "test-user-123",
            permission: SharePermission.Read);

        context.FileShares.Add(share);
        await context.SaveChangesAsync();

        return share.Id;
    }

    #endregion
}

#region Request/Response DTOs

/// <summary>
/// EN: Create share request DTO for tests.
/// VI: DTO request tạo share cho tests.
/// </summary>
public record CreateShareRequest(
    Guid FileId,
    string Permission,
    string? SharedWith = null,
    string? Password = null,
    DateTime? ExpiresAt = null,
    int? MaxDownloads = null);

/// <summary>
/// EN: Create share response DTO for tests.
/// VI: DTO response tạo share cho tests.
/// </summary>
public record CreateShareResponse(
    Guid ShareId,
    string ShareToken,
    string ShareUrl);

#endregion
