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

namespace LittleShop.Tests.Integration;

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

    public OrdersControllerTests(TestWebApplicationFactory factory)
    {
        _factory = factory;
        _client = _factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
        
        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
            Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() }
        };
    }

    [Fact]
    public async Task GetAllOrders_WithAdminToken_ReturnsAllOrders()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateAdminJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        await SeedTestOrders();

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

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        orders.Should().NotBeNull();
        orders.Should().HaveCountGreaterThan(0);
    }

    [Fact]
    public async Task GetAllOrders_WithUserToken_ReturnsUnauthorized()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken(role: "User");
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        // Act
        var response = await _client.GetAsync("/api/orders");

        // Assert
        response.StatusCode.Should().BeOneOf(HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden);
    }

    [Fact]
    public async Task GetOrderById_WithAdminToken_ReturnsOrder()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateAdminJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var orderId = await SeedOrderAndGetId();

        // Act
        var response = await _client.GetAsync($"/api/orders/{orderId}");
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        var order = JsonSerializer.Deserialize<OrderDto>(content, _jsonOptions);

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

    [Fact]
    public async Task GetOrdersByIdentity_WithValidToken_ReturnsOrders()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var identityReference = "test-identity-" + Guid.NewGuid();
        await SeedOrdersForIdentity(identityReference);

        // Act
        var response = await _client.GetAsync($"/api/orders/by-identity/{identityReference}");
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        var orders = JsonSerializer.Deserialize<List<OrderDto>>(content, _jsonOptions);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        orders.Should().NotBeNull();
        orders.Should().OnlyContain(o => o.IdentityReference == identityReference);
    }

    [Fact]
    public async Task CreateOrder_WithValidData_ReturnsCreatedOrder()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var productIds = await SeedProductsAndGetIds();
        
        var createOrderDto = new CreateOrderDto
        {
            IdentityReference = "test-identity-" + Guid.NewGuid(),
            ShippingName = "John Doe",
            ShippingAddress = "123 Test Street",
            ShippingCity = "Test City",
            ShippingPostCode = "TE5 7CD",
            ShippingCountry = "United Kingdom",
            Items = new List<CreateOrderItemDto>
            {
                new CreateOrderItemDto { ProductId = productIds[0], Quantity = 2 },
                new CreateOrderItemDto { ProductId = productIds[1], Quantity = 1 }
            }
        };

        var json = JsonSerializer.Serialize(createOrderDto, _jsonOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/api/orders", content);
        response.EnsureSuccessStatusCode();
        
        var responseContent = await response.Content.ReadAsStringAsync();
        var order = JsonSerializer.Deserialize<OrderDto>(responseContent, _jsonOptions);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);
        order.Should().NotBeNull();
        order!.IdentityReference.Should().Be(createOrderDto.IdentityReference);
        order.Items.Should().HaveCount(2);
    }

    [Fact]
    public async Task CreateOrder_WithInvalidProductId_ReturnsBadRequest()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        
        var createOrderDto = new CreateOrderDto
        {
            IdentityReference = "test-identity-" + Guid.NewGuid(),
            ShippingName = "John Doe",
            ShippingAddress = "123 Test Street",
            ShippingCity = "Test City",
            ShippingPostCode = "TE5 7CD",
            ShippingCountry = "United Kingdom",
            Items = new List<CreateOrderItemDto>
            {
                new CreateOrderItemDto { ProductId = Guid.NewGuid(), Quantity = 1 }
            }
        };

        var json = JsonSerializer.Serialize(createOrderDto, _jsonOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/api/orders", content);

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

    [Fact]
    public async Task UpdateOrderStatus_WithAdminToken_UpdatesStatus()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateAdminJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var orderId = await SeedOrderAndGetId();
        
        var updateDto = new UpdateOrderStatusDto
        {
            Status = OrderStatus.Shipped,
            TrackingNumber = "TRACK123456"
        };

        var json = JsonSerializer.Serialize(updateDto, _jsonOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PutAsync($"/api/orders/{orderId}/status", content);

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

    [Fact]
    public async Task CreatePayment_WithValidOrder_ReturnsPaymentInfo()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var orderId = await SeedOrderAndGetId();
        
        var createPaymentDto = new CreatePaymentDto
        {
            Currency = CryptoCurrency.BTC
        };

        var json = JsonSerializer.Serialize(createPaymentDto, _jsonOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync($"/api/orders/{orderId}/payments", content);
        
        // Assert - May return error if BTCPay not configured, but should authenticate
        response.StatusCode.Should().NotBe(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task CancelOrder_WithValidIdentity_CancelsOrder()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var identityReference = "test-identity-" + Guid.NewGuid();
        var orderId = await SeedOrderAndGetId(identityReference);
        
        var cancelDto = new CancelOrderDto
        {
            IdentityReference = identityReference
        };

        var json = JsonSerializer.Serialize(cancelDto, _jsonOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync($"/api/orders/{orderId}/cancel", content);

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

    [Fact]
    public async Task CancelOrder_WithWrongIdentity_ReturnsBadRequest()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var orderId = await SeedOrderAndGetId("correct-identity");
        
        var cancelDto = new CancelOrderDto
        {
            IdentityReference = "wrong-identity"
        };

        var json = JsonSerializer.Serialize(cancelDto, _jsonOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync($"/api/orders/{orderId}/cancel", content);

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

    [Fact]
    public async Task PaymentWebhook_WithValidData_ReturnsOk()
    {
        // Arrange
        var token = JwtTokenHelper.GenerateJwtToken();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        
        var webhookDto = new PaymentWebhookDto
        {
            InvoiceId = "INV123456",
            Status = PaymentStatus.Paid,
            Amount = 100.00m,
            TransactionHash = "tx123456789"
        };

        var json = JsonSerializer.Serialize(webhookDto, _jsonOptions);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/api/orders/payments/webhook", content);

        // Assert - Will return BadRequest if invoice not found, but should authenticate
        response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.BadRequest);
    }

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

        // Add test category and product
        var category = new Category 
        { 
            Id = Guid.NewGuid(), 
            Name = "Test Category", 
            Description = "Test",
            IsActive = true 
        };
        context.Categories.Add(category);

        var product = new Product 
        { 
            Id = Guid.NewGuid(), 
            Name = "Test Product", 
            Description = "Test",
            Price = 99.99m,
            CategoryId = category.Id,
            IsActive = true,
            Weight = 1,
            WeightUnit = ProductWeightUnit.Kilograms
        };
        context.Products.Add(product);

        // Add test orders
        var orders = new[]
        {
            new Order 
            { 
                Id = Guid.NewGuid(),
                IdentityReference = "test-identity-1",
                Status = OrderStatus.PendingPayment,
                ShippingName = "Customer 1",
                ShippingAddress = "Address 1",
                ShippingCity = "City 1",
                ShippingPostCode = "PC1",
                ShippingCountry = "Country 1",
                TotalAmount = 99.99m,
                CreatedAt = DateTime.UtcNow
            },
            new Order 
            { 
                Id = Guid.NewGuid(),
                IdentityReference = "test-identity-2",
                Status = OrderStatus.Processing,
                ShippingName = "Customer 2",
                ShippingAddress = "Address 2",
                ShippingCity = "City 2",
                ShippingPostCode = "PC2",
                ShippingCountry = "Country 2",
                TotalAmount = 199.99m,
                CreatedAt = DateTime.UtcNow
            }
        };
        
        context.Orders.AddRange(orders);
        await context.SaveChangesAsync();
    }

    private async Task<Guid> SeedOrderAndGetId(string identityReference = "test-identity")
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
        
        // Add category and product if not exists
        var category = context.Categories.FirstOrDefault() ?? new Category 
        { 
            Id = Guid.NewGuid(), 
            Name = "Test Category", 
            Description = "Test",
            IsActive = true 
        };
        if (!context.Categories.Any())
            context.Categories.Add(category);

        var product = context.Products.FirstOrDefault() ?? new Product 
        { 
            Id = Guid.NewGuid(), 
            Name = "Test Product", 
            Description = "Test",
            Price = 99.99m,
            CategoryId = category.Id,
            IsActive = true,
            Weight = 1,
            WeightUnit = ProductWeightUnit.Kilograms
        };
        if (!context.Products.Any())
            context.Products.Add(product);

        var order = new Order 
        { 
            Id = Guid.NewGuid(),
            IdentityReference = identityReference,
            Status = OrderStatus.PendingPayment,
            ShippingName = "Test Customer",
            ShippingAddress = "Test Address",
            ShippingCity = "Test City",
            ShippingPostCode = "TE5 7CD",
            ShippingCountry = "United Kingdom",
            TotalAmount = 99.99m,
            CreatedAt = DateTime.UtcNow
        };
        
        context.Orders.Add(order);
        await context.SaveChangesAsync();
        
        return order.Id;
    }

    private async Task SeedOrdersForIdentity(string identityReference)
    {
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
        
        var orders = new[]
        {
            new Order 
            { 
                Id = Guid.NewGuid(),
                IdentityReference = identityReference,
                Status = OrderStatus.PendingPayment,
                ShippingName = "Customer",
                ShippingAddress = "Address",
                ShippingCity = "City",
                ShippingPostCode = "PC",
                ShippingCountry = "Country",
                TotalAmount = 50.00m,
                CreatedAt = DateTime.UtcNow
            },
            new Order 
            { 
                Id = Guid.NewGuid(),
                IdentityReference = identityReference,
                Status = OrderStatus.Shipped,
                ShippingName = "Customer",
                ShippingAddress = "Address",
                ShippingCity = "City",
                ShippingPostCode = "PC",
                ShippingCountry = "Country",
                TotalAmount = 75.00m,
                CreatedAt = DateTime.UtcNow.AddDays(-5)
            }
        };
        
        context.Orders.AddRange(orders);
        await context.SaveChangesAsync();
    }

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

        var products = new[]
        {
            new Product 
            { 
                Id = Guid.NewGuid(), 
                Name = "Product 1", 
                Description = "Test",
                Price = 25.00m,
                CategoryId = category.Id,
                IsActive = true,
                Weight = 0.5m,
                WeightUnit = ProductWeightUnit.Kilograms
            },
            new Product 
            { 
                Id = Guid.NewGuid(), 
                Name = "Product 2", 
                Description = "Test",
                Price = 35.00m,
                CategoryId = category.Id,
                IsActive = true,
                Weight = 1.0m,
                WeightUnit = ProductWeightUnit.Kilograms
            }
        };
        
        context.Products.AddRange(products);
        await context.SaveChangesAsync();
        
        return products.Select(p => p.Id).ToList();
    }
}