using FluentAssertions;
using LittleShop.Data;
using LittleShop.DTOs;
using LittleShop.Models;
using LittleShop.Services;
using Microsoft.EntityFrameworkCore;
using Moq;
using AutoMapper;
using Xunit;
using LittleShop.Mapping;

namespace LittleShop.Tests.Unit;

public class CategoryServiceTests : IDisposable
{
    private readonly LittleShopContext _context;
    private readonly ICategoryService _categoryService;

    public CategoryServiceTests()
    {
        // Set up in-memory database
        var options = new DbContextOptionsBuilder<LittleShopContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;
        _context = new LittleShopContext(options);

        // Create service
        _categoryService = new CategoryService(_context);
    }

    [Fact]
    public async Task GetAllCategoriesAsync_ReturnsAllCategories()
    {
        // Arrange
        var categories = new[]
        {
            new Category { Id = Guid.NewGuid(), Name = "Category 1", Description = "Desc 1", IsActive = true },
            new Category { Id = Guid.NewGuid(), Name = "Category 2", Description = "Desc 2", IsActive = true },
            new Category { Id = Guid.NewGuid(), Name = "Category 3", Description = "Desc 3", IsActive = false }
        };
        
        _context.Categories.AddRange(categories);
        await _context.SaveChangesAsync();

        // Act
        var result = await _categoryService.GetAllCategoriesAsync();

        // Assert
        result.Should().HaveCount(3);
        result.Should().Contain(c => c.Name == "Category 1");
        result.Should().Contain(c => c.Name == "Category 2");
        result.Should().Contain(c => c.Name == "Category 3");
    }

    [Fact]
    public async Task GetCategoryByIdAsync_WithValidId_ReturnsCategory()
    {
        // Arrange
        var categoryId = Guid.NewGuid();
        var category = new Category 
        { 
            Id = categoryId, 
            Name = "Test Category", 
            Description = "Test Description", 
            IsActive = true 
        };
        
        _context.Categories.Add(category);
        await _context.SaveChangesAsync();

        // Act
        var result = await _categoryService.GetCategoryByIdAsync(categoryId);

        // Assert
        result.Should().NotBeNull();
        result!.Id.Should().Be(categoryId);
        result.Name.Should().Be("Test Category");
        result.Description.Should().Be("Test Description");
    }

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

        // Act
        var result = await _categoryService.GetCategoryByIdAsync(invalidId);

        // Assert
        result.Should().BeNull();
    }

    [Fact]
    public async Task CreateCategoryAsync_WithValidData_CreatesCategory()
    {
        // Arrange
        var createDto = new CreateCategoryDto
        {
            Name = "New Category",
            Description = "New Description"
        };

        // Act
        var result = await _categoryService.CreateCategoryAsync(createDto);

        // Assert
        result.Should().NotBeNull();
        result.Name.Should().Be("New Category");
        result.Description.Should().Be("New Description");
        result.IsActive.Should().BeTrue();

        // Verify in database
        var dbCategory = await _context.Categories.FindAsync(result.Id);
        dbCategory.Should().NotBeNull();
        dbCategory!.Name.Should().Be("New Category");
    }

    [Fact]
    public async Task UpdateCategoryAsync_WithValidData_UpdatesCategory()
    {
        // Arrange
        var categoryId = Guid.NewGuid();
        var category = new Category 
        { 
            Id = categoryId, 
            Name = "Original Name", 
            Description = "Original Description", 
            IsActive = true 
        };
        
        _context.Categories.Add(category);
        await _context.SaveChangesAsync();

        var updateDto = new UpdateCategoryDto
        {
            Name = "Updated Name",
            Description = "Updated Description",
            IsActive = false
        };

        // Act
        var result = await _categoryService.UpdateCategoryAsync(categoryId, updateDto);

        // Assert
        result.Should().BeTrue();

        // Verify in database
        var dbCategory = await _context.Categories.FindAsync(categoryId);
        dbCategory!.Name.Should().Be("Updated Name");
        dbCategory.Description.Should().Be("Updated Description");
        dbCategory.IsActive.Should().BeFalse();
    }

    [Fact]
    public async Task UpdateCategoryAsync_WithInvalidId_ReturnsFalse()
    {
        // Arrange
        var invalidId = Guid.NewGuid();
        var updateDto = new UpdateCategoryDto
        {
            Name = "Updated Name",
            Description = "Updated Description",
            IsActive = true
        };

        // Act
        var result = await _categoryService.UpdateCategoryAsync(invalidId, updateDto);

        // Assert
        result.Should().BeFalse();
    }

    [Fact]
    public async Task DeleteCategoryAsync_WithValidId_DeletesCategory()
    {
        // Arrange
        var categoryId = Guid.NewGuid();
        var category = new Category 
        { 
            Id = categoryId, 
            Name = "To Delete", 
            Description = "Will be deleted", 
            IsActive = true 
        };
        
        _context.Categories.Add(category);
        await _context.SaveChangesAsync();

        // Act
        var result = await _categoryService.DeleteCategoryAsync(categoryId);

        // Assert
        result.Should().BeTrue();

        // Verify in database - soft delete means IsActive = false
        var dbCategory = await _context.Categories.FindAsync(categoryId);
        dbCategory.Should().NotBeNull();
        dbCategory!.IsActive.Should().BeFalse();
    }

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

        // Act
        var result = await _categoryService.DeleteCategoryAsync(invalidId);

        // Assert
        result.Should().BeFalse();
    }

    [Fact]
    public async Task DeleteCategoryAsync_WithProductsAttached_SoftDeletesCategory()
    {
        // Arrange
        var categoryId = Guid.NewGuid();
        var category = new Category 
        { 
            Id = categoryId, 
            Name = "Category with Products", 
            Description = "Has products", 
            IsActive = true 
        };
        
        var product = new Product
        {
            Id = Guid.NewGuid(),
            Name = "Product",
            Description = "Product in category",
            Price = 10.00m,
            CategoryId = categoryId,
            IsActive = true,
            Weight = 1,
            WeightUnit = Enums.ProductWeightUnit.Kilograms
        };
        
        _context.Categories.Add(category);
        _context.Products.Add(product);
        await _context.SaveChangesAsync();

        // Act
        var result = await _categoryService.DeleteCategoryAsync(categoryId);

        // Assert - soft delete should succeed even with products
        result.Should().BeTrue();
        
        // Verify category is soft deleted
        var dbCategory = await _context.Categories.FindAsync(categoryId);
        dbCategory.Should().NotBeNull();
        dbCategory!.IsActive.Should().BeFalse();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}