using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using FluentAssertions;
using LittleShop.Data;
using LittleShop.DTOs;
using LittleShop.Models;
using LittleShop.Tests.Infrastructure;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace LittleShop.Tests.Integration;

public class CatalogControllerTests : IClassFixture<TestWebApplicationFactory>
{
    private readonly HttpClient _client;
    private readonly TestWebApplicationFactory _factory;
    private readonly JsonSerializerOptions _jsonOptions;

    public CatalogControllerTests(TestWebApplicationFactory factory)
    {
        _factory = factory;
        _client = _factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
        
        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };

        // Add default JWT token for authenticated requests
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    }

    [Fact]
    public async Task GetCategories_WithAuthentication_ReturnsOnlyActiveCategories()
    {
        // Arrange
        await SeedTestData();

        // Act
        var response = await _client.GetAsync("/api/catalog/categories");
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        var categories = JsonSerializer.Deserialize<List<CategoryDto>>(content, _jsonOptions);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        categories.Should().NotBeNull();
        categories.Should().HaveCountGreaterThan(0);
        categories.Should().OnlyContain(c => c.IsActive);
    }

    [Fact]
    public async Task GetCategoryById_WithValidId_ReturnsCategory()
    {
        // Arrange
        var categoryId = await SeedCategoryAndGetId("Test Category");

        // Act
        var response = await _client.GetAsync($"/api/catalog/categories/{categoryId}");
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        var category = JsonSerializer.Deserialize<CategoryDto>(content, _jsonOptions);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        category.Should().NotBeNull();
        category!.Id.Should().Be(categoryId);
        category.Name.Should().Be("Test Category");
    }

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

        // Act
        var response = await _client.GetAsync($"/api/catalog/categories/{invalidId}");

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

    [Fact]
    public async Task GetCategoryById_WithInactiveCategory_Returns404()
    {
        // Arrange
        var categoryId = await SeedCategoryAndGetId("Inactive Category", isActive: false);

        // Act
        var response = await _client.GetAsync($"/api/catalog/categories/{categoryId}");

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

    [Fact]
    public async Task GetProducts_WithoutCategoryFilter_ReturnsAllActiveProducts()
    {
        // Arrange
        await SeedTestData();

        // Act
        var response = await _client.GetAsync("/api/catalog/products");
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        var pagedResult = JsonSerializer.Deserialize<PagedResult<ProductDto>>(content, _jsonOptions);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        pagedResult.Should().NotBeNull();
        pagedResult!.Items.Should().HaveCountGreaterThan(0);
        pagedResult.TotalCount.Should().BeGreaterThan(0);
    }

    [Fact]
    public async Task GetProducts_WithCategoryFilter_ReturnsOnlyProductsInCategory()
    {
        // Arrange
        var categoryId = await SeedCategoryWithProducts();

        // Act
        var response = await _client.GetAsync($"/api/catalog/products?categoryId={categoryId}");
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        var pagedResult = JsonSerializer.Deserialize<PagedResult<ProductDto>>(content, _jsonOptions);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        pagedResult.Should().NotBeNull();
        pagedResult!.Items.Should().HaveCountGreaterThan(0);
        pagedResult.Items.Should().OnlyContain(p => p.CategoryId == categoryId);
    }

    [Fact]
    public async Task GetProductById_WithValidId_ReturnsProduct()
    {
        // Arrange
        var productId = await SeedProductAndGetId();

        // Act
        var response = await _client.GetAsync($"/api/catalog/products/{productId}");
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        var product = JsonSerializer.Deserialize<ProductDto>(content, _jsonOptions);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        product.Should().NotBeNull();
        product!.Id.Should().Be(productId);
    }

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

        // Act
        var response = await _client.GetAsync($"/api/catalog/products/{invalidId}");

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

    [Fact]
    public async Task GetProductById_WithInactiveProduct_Returns404()
    {
        // Arrange
        var productId = await SeedProductAndGetId(isActive: false);

        // Act
        var response = await _client.GetAsync($"/api/catalog/products/{productId}");

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

    private async Task SeedTestData()
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
        
        // Clear existing data
        context.Categories.RemoveRange(context.Categories);
        context.Products.RemoveRange(context.Products);
        await context.SaveChangesAsync();

        // Add test categories
        var categories = new[]
        {
            new Category { Id = Guid.NewGuid(), Name = "Electronics", Description = "Electronic devices", IsActive = true },
            new Category { Id = Guid.NewGuid(), Name = "Books", Description = "Books and literature", IsActive = true },
            new Category { Id = Guid.NewGuid(), Name = "Archived", Description = "Archived category", IsActive = false }
        };
        
        context.Categories.AddRange(categories);

        // Add test products
        var products = new[]
        {
            new Product 
            { 
                Id = Guid.NewGuid(), 
                Name = "Laptop", 
                Description = "High-performance laptop",
                Price = 999.99m,
                CategoryId = categories[0].Id,
                IsActive = true,
                Weight = 2.5m,
                WeightUnit = Enums.ProductWeightUnit.Kilograms
            },
            new Product 
            { 
                Id = Guid.NewGuid(), 
                Name = "Programming Book", 
                Description = "Learn programming",
                Price = 49.99m,
                CategoryId = categories[1].Id,
                IsActive = true,
                Weight = 500,
                WeightUnit = Enums.ProductWeightUnit.Grams
            }
        };
        
        context.Products.AddRange(products);
        await context.SaveChangesAsync();
    }

    private async Task<Guid> SeedCategoryAndGetId(string name, bool isActive = true)
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
        
        var category = new Category 
        { 
            Id = Guid.NewGuid(), 
            Name = name, 
            Description = "Test category",
            IsActive = isActive 
        };
        
        context.Categories.Add(category);
        await context.SaveChangesAsync();
        
        return category.Id;
    }

    private async Task<Guid> SeedCategoryWithProducts()
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
        
        var category = new Category 
        { 
            Id = Guid.NewGuid(), 
            Name = "Test Category", 
            Description = "Category with products",
            IsActive = true 
        };
        
        context.Categories.Add(category);

        var products = new[]
        {
            new Product 
            { 
                Id = Guid.NewGuid(), 
                Name = "Product 1", 
                Description = "First product",
                Price = 19.99m,
                CategoryId = category.Id,
                IsActive = true,
                Weight = 100,
                WeightUnit = Enums.ProductWeightUnit.Grams
            },
            new Product 
            { 
                Id = Guid.NewGuid(), 
                Name = "Product 2", 
                Description = "Second product",
                Price = 29.99m,
                CategoryId = category.Id,
                IsActive = true,
                Weight = 200,
                WeightUnit = Enums.ProductWeightUnit.Grams
            }
        };
        
        context.Products.AddRange(products);
        await context.SaveChangesAsync();
        
        return category.Id;
    }

    private async Task<Guid> SeedProductAndGetId(bool isActive = true)
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
        
        var category = new Category 
        { 
            Id = Guid.NewGuid(), 
            Name = "Product Category", 
            Description = "Category for product",
            IsActive = true 
        };
        
        context.Categories.Add(category);

        var product = new Product 
        { 
            Id = Guid.NewGuid(), 
            Name = "Test Product", 
            Description = "Test product description",
            Price = 99.99m,
            CategoryId = category.Id,
            IsActive = isActive,
            Weight = 1.5m,
            WeightUnit = Enums.ProductWeightUnit.Kilograms
        };
        
        context.Products.Add(product);
        await context.SaveChangesAsync();
        
        return product.Id;
    }
}