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.Infrastructure.Persistence;

namespace StorageService.FunctionalTests.ApiTests;

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

    public FilesApiTests(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 GetFile Tests

    [Fact]
    public async Task GetFile_ExistingFile_Returns200WithFileMetadata()
    {
        // Arrange
        var fileId = await SeedTestFile("test-file.pdf");

        // Act
        var response = await _client.GetAsync($"/api/v1/storage/files/{fileId}");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        
        var content = await response.Content.ReadFromJsonAsync<FileMetadataResponse>();
        content.Should().NotBeNull();
        content!.FileName.Should().Be("test-file.pdf");
    }

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

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

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

    [Fact]
    public async Task GetFile_InvalidId_Returns400()
    {
        // Act
        var response = await _client.GetAsync("/api/v1/storage/files/invalid-guid");

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

    #endregion

    #region GetUserFiles Tests

    [Fact]
    public async Task GetUserFiles_WithFiles_ReturnsFileList()
    {
        // Arrange
        await SeedTestFile("file1.pdf");
        await SeedTestFile("file2.pdf");

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

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        
        var content = await response.Content.ReadFromJsonAsync<UserFilesResponse>();
        content.Should().NotBeNull();
        content!.Files.Should().NotBeEmpty();
    }

    [Fact]
    public async Task GetUserFiles_WithPagination_RespectsPageSize()
    {
        // Arrange
        for (int i = 0; i < 5; i++)
        {
            await SeedTestFile($"file{i}.pdf");
        }

        // Act
        var response = await _client.GetAsync("/api/v1/storage/files?page=1&pageSize=2");

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

    #endregion

    #region DeleteFile Tests

    [Fact]
    public async Task DeleteFile_ExistingFile_Returns204()
    {
        // Arrange
        var fileId = await SeedTestFile("to-delete.pdf");

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

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

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

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

        // Assert
        // EN: Could be 404 or 400 depending on implementation
        // VI: Có thể là 404 hoặc 400 tùy vào implementation
        response.IsSuccessStatusCode.Should().BeFalse();
    }

    [Fact]
    public async Task DeleteFile_AlreadyDeleted_ReturnsError()
    {
        // Arrange
        var fileId = await SeedTestFile("already-deleted.pdf", isDeleted: true);

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

        // Assert
        response.IsSuccessStatusCode.Should().BeFalse();
    }

    #endregion

    #region GetDownloadUrl Tests

    [Fact]
    public async Task GetDownloadUrl_ExistingFile_ReturnsUrl()
    {
        // Arrange
        var fileId = await SeedTestFile("downloadable.pdf");

        // Act
        var response = await _client.GetAsync($"/api/v1/storage/files/{fileId}/download-url");

        // Assert
        // EN: May succeed or fail depending on storage provider mock
        // VI: Có thể thành công hoặc thất bại tùy thuộc vào mock storage provider
        // In real functional tests, we'd have proper MinIO mocking
    }

    #endregion

    #region Helper Methods

    private async Task<Guid> SeedTestFile(string fileName, bool isDeleted = false)
    {
        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);

        if (isDeleted)
        {
            file.Delete();
        }

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

        return file.Id;
    }

    #endregion
}

#region Response DTOs

/// <summary>
/// EN: File metadata response DTO for tests.
/// VI: DTO response metadata file cho tests.
/// </summary>
public record FileMetadataResponse(
    Guid Id,
    string FileName,
    string ContentType,
    long FileSizeBytes,
    string UserId,
    string AccessLevel,
    DateTime UploadedAt);

/// <summary>
/// EN: User files list response DTO for tests.
/// VI: DTO response danh sách files của user cho tests.
/// </summary>
public record UserFilesResponse(
    IEnumerable<FileMetadataResponse> Files,
    int TotalCount,
    int Page,
    int PageSize);

#endregion
