using Microsoft.EntityFrameworkCore;
using LittleShop.Data;
using LittleShop.Models;
using LittleShop.DTOs;

namespace LittleShop.Services;

public class ProductService : IProductService
{
    private readonly LittleShopContext _context;
    private readonly IWebHostEnvironment _environment;

    public ProductService(LittleShopContext context, IWebHostEnvironment environment)
    {
        _context = context;
        _environment = environment;
    }

    public async Task<IEnumerable<ProductDto>> GetAllProductsAsync()
    {
        return await _context.Products
            .Include(p => p.Category)
            .Include(p => p.Photos)
            .Include(p => p.Variations.Where(v => v.IsActive))
            .Where(p => p.IsActive)
            .Select(p => new ProductDto
            {
                Id = p.Id,
                Name = p.Name,
                Description = p.Description,
                Price = p.Price,
                Weight = p.Weight,
                WeightUnit = p.WeightUnit,
                StockQuantity = p.StockQuantity,
                CategoryId = p.CategoryId,
                CategoryName = p.Category.Name,
                CreatedAt = p.CreatedAt,
                UpdatedAt = p.UpdatedAt,
                IsActive = p.IsActive,
                Photos = p.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto
                {
                    Id = ph.Id,
                    FileName = ph.FileName,
                    FilePath = ph.FilePath,
                    AltText = ph.AltText,
                    SortOrder = ph.SortOrder
                }).ToList(),
                Variations = p.Variations.OrderBy(v => v.SortOrder).Select(v => new ProductVariationDto
                {
                    Id = v.Id,
                    ProductId = v.ProductId,
                    Name = v.Name,
                    Description = v.Description,
                    Quantity = v.Quantity,
                    Price = v.Price,
                    PricePerUnit = v.PricePerUnit,
                    SortOrder = v.SortOrder,
                    IsActive = v.IsActive,
                    CreatedAt = v.CreatedAt,
                    UpdatedAt = v.UpdatedAt
                }).ToList()
            })
            .ToListAsync();
    }

    public async Task<IEnumerable<ProductDto>> GetProductsByCategoryAsync(Guid categoryId)
    {
        return await _context.Products
            .Include(p => p.Category)
            .Include(p => p.Photos)
            .Include(p => p.Variations.Where(v => v.IsActive))
            .Where(p => p.IsActive && p.CategoryId == categoryId)
            .Select(p => new ProductDto
            {
                Id = p.Id,
                Name = p.Name,
                Description = p.Description,
                Price = p.Price,
                Weight = p.Weight,
                WeightUnit = p.WeightUnit,
                StockQuantity = p.StockQuantity,
                CategoryId = p.CategoryId,
                CategoryName = p.Category.Name,
                CreatedAt = p.CreatedAt,
                UpdatedAt = p.UpdatedAt,
                IsActive = p.IsActive,
                Photos = p.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto
                {
                    Id = ph.Id,
                    FileName = ph.FileName,
                    FilePath = ph.FilePath,
                    AltText = ph.AltText,
                    SortOrder = ph.SortOrder
                }).ToList(),
                Variations = p.Variations.OrderBy(v => v.SortOrder).Select(v => new ProductVariationDto
                {
                    Id = v.Id,
                    ProductId = v.ProductId,
                    Name = v.Name,
                    Description = v.Description,
                    Quantity = v.Quantity,
                    Price = v.Price,
                    PricePerUnit = v.PricePerUnit,
                    SortOrder = v.SortOrder,
                    IsActive = v.IsActive,
                    CreatedAt = v.CreatedAt,
                    UpdatedAt = v.UpdatedAt
                }).ToList()
            })
            .ToListAsync();
    }

    public async Task<ProductDto?> GetProductByIdAsync(Guid id)
    {
        var product = await _context.Products
            .Include(p => p.Category)
            .Include(p => p.Photos)
            .Include(p => p.Variations.Where(v => v.IsActive))
            .FirstOrDefaultAsync(p => p.Id == id);

        if (product == null) return null;

        return new ProductDto
        {
            Id = product.Id,
            Name = product.Name,
            Description = product.Description,
            Price = product.Price,
            Weight = product.Weight,
            WeightUnit = product.WeightUnit,
            StockQuantity = product.StockQuantity,
            CategoryId = product.CategoryId,
            CategoryName = product.Category.Name,
            CreatedAt = product.CreatedAt,
            UpdatedAt = product.UpdatedAt,
            IsActive = product.IsActive,
            Photos = product.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto
            {
                Id = ph.Id,
                FileName = ph.FileName,
                FilePath = ph.FilePath,
                AltText = ph.AltText,
                SortOrder = ph.SortOrder
            }).ToList(),
            Variations = product.Variations.OrderBy(v => v.SortOrder).Select(v => new ProductVariationDto
            {
                Id = v.Id,
                ProductId = v.ProductId,
                Name = v.Name,
                Description = v.Description,
                Quantity = v.Quantity,
                Price = v.Price,
                PricePerUnit = v.PricePerUnit,
                SortOrder = v.SortOrder,
                IsActive = v.IsActive,
                CreatedAt = v.CreatedAt,
                UpdatedAt = v.UpdatedAt
            }).ToList()
        };
    }

    public async Task<ProductDto> CreateProductAsync(CreateProductDto createProductDto)
    {
        var product = new Product
        {
            Id = Guid.NewGuid(),
            Name = createProductDto.Name,
            Description = string.IsNullOrEmpty(createProductDto.Description) ? " " : createProductDto.Description,
            Price = createProductDto.Price,
            Weight = createProductDto.Weight,
            WeightUnit = createProductDto.WeightUnit,
            StockQuantity = createProductDto.StockQuantity,
            CategoryId = createProductDto.CategoryId,
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow,
            IsActive = true
        };

        _context.Products.Add(product);
        await _context.SaveChangesAsync();

        var category = await _context.Categories.FindAsync(createProductDto.CategoryId);

        return new ProductDto
        {
            Id = product.Id,
            Name = product.Name,
            Description = product.Description,
            Price = product.Price,
            Weight = product.Weight,
            WeightUnit = product.WeightUnit,
            CategoryId = product.CategoryId,
            CategoryName = category?.Name ?? "",
            CreatedAt = product.CreatedAt,
            UpdatedAt = product.UpdatedAt,
            IsActive = product.IsActive,
            Photos = new List<ProductPhotoDto>(),
            Variations = new List<ProductVariationDto>()
        };
    }

    public async Task<bool> UpdateProductAsync(Guid id, UpdateProductDto updateProductDto)
    {
        var product = await _context.Products.FindAsync(id);
        if (product == null) return false;

        if (!string.IsNullOrEmpty(updateProductDto.Name))
            product.Name = updateProductDto.Name;

        if (!string.IsNullOrEmpty(updateProductDto.Description))
            product.Description = updateProductDto.Description;

        if (updateProductDto.Price.HasValue)
            product.Price = updateProductDto.Price.Value;

        if (updateProductDto.Weight.HasValue)
            product.Weight = updateProductDto.Weight.Value;

        if (updateProductDto.WeightUnit.HasValue)
            product.WeightUnit = updateProductDto.WeightUnit.Value;

        if (updateProductDto.StockQuantity.HasValue)
            product.StockQuantity = updateProductDto.StockQuantity.Value;

        if (updateProductDto.CategoryId.HasValue)
            product.CategoryId = updateProductDto.CategoryId.Value;

        if (updateProductDto.IsActive.HasValue)
            product.IsActive = updateProductDto.IsActive.Value;

        product.UpdatedAt = DateTime.UtcNow;

        await _context.SaveChangesAsync();
        return true;
    }

    public async Task<bool> DeleteProductAsync(Guid id)
    {
        var product = await _context.Products.FindAsync(id);
        if (product == null) return false;

        product.IsActive = false;
        await _context.SaveChangesAsync();
        return true;
    }

    public async Task<bool> AddProductPhotoAsync(Guid productId, IFormFile file, string? altText = null)
    {
        var product = await _context.Products.FindAsync(productId);
        if (product == null) return false;

        var uploadsPath = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", "products");
        Directory.CreateDirectory(uploadsPath);

        var fileName = $"{Guid.NewGuid()}_{file.FileName}";
        var filePath = Path.Combine(uploadsPath, fileName);

        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }

        var maxSortOrder = await _context.ProductPhotos
            .Where(pp => pp.ProductId == productId)
            .Select(pp => (int?)pp.SortOrder)
            .MaxAsync() ?? 0;

        var productPhoto = new ProductPhoto
        {
            Id = Guid.NewGuid(),
            ProductId = productId,
            FileName = fileName,
            FilePath = $"/uploads/products/{fileName}",
            AltText = altText,
            SortOrder = maxSortOrder + 1,
            CreatedAt = DateTime.UtcNow
        };

        _context.ProductPhotos.Add(productPhoto);
        await _context.SaveChangesAsync();
        return true;
    }

    public async Task<bool> RemoveProductPhotoAsync(Guid productId, Guid photoId)
    {
        var photo = await _context.ProductPhotos
            .FirstOrDefaultAsync(pp => pp.Id == photoId && pp.ProductId == productId);

        if (photo == null) return false;

        var physicalPath = Path.Combine(_environment.WebRootPath, photo.FilePath.TrimStart('/'));
        if (File.Exists(physicalPath))
        {
            File.Delete(physicalPath);
        }

        _context.ProductPhotos.Remove(photo);
        await _context.SaveChangesAsync();
        return true;
    }

    public async Task<ProductPhotoDto?> AddProductPhotoAsync(CreateProductPhotoDto photoDto)
    {
        var product = await _context.Products.FindAsync(photoDto.ProductId);
        if (product == null) return null;

        var existingPhotos = await _context.ProductPhotos
            .Where(pp => pp.ProductId == photoDto.ProductId)
            .ToListAsync();
        
        var maxSortOrder = existingPhotos.Any() ? existingPhotos.Max(pp => pp.SortOrder) : 0;

        var productPhoto = new ProductPhoto
        {
            Id = Guid.NewGuid(),
            ProductId = photoDto.ProductId,
            FileName = Path.GetFileName(photoDto.PhotoUrl),
            FilePath = photoDto.PhotoUrl,
            AltText = photoDto.AltText,
            SortOrder = photoDto.DisplayOrder > 0 ? photoDto.DisplayOrder : maxSortOrder + 1,
            CreatedAt = DateTime.UtcNow
        };

        _context.ProductPhotos.Add(productPhoto);
        await _context.SaveChangesAsync();

        return new ProductPhotoDto
        {
            Id = productPhoto.Id,
            FileName = productPhoto.FileName,
            FilePath = productPhoto.FilePath,
            AltText = productPhoto.AltText,
            SortOrder = productPhoto.SortOrder
        };
    }

    public async Task<IEnumerable<ProductDto>> SearchProductsAsync(string searchTerm)
    {
        var query = _context.Products
            .Include(p => p.Category)
            .Include(p => p.Photos)
            .Include(p => p.Variations.Where(v => v.IsActive))
            .Where(p => p.IsActive);

        if (!string.IsNullOrWhiteSpace(searchTerm))
        {
            searchTerm = searchTerm.ToLower();
            query = query.Where(p => 
                p.Name.ToLower().Contains(searchTerm) || 
                p.Description.ToLower().Contains(searchTerm));
        }

        return await query.Select(p => new ProductDto
        {
            Id = p.Id,
            Name = p.Name,
            Description = p.Description,
            Price = p.Price,
            Weight = p.Weight,
            WeightUnit = p.WeightUnit,
            StockQuantity = p.StockQuantity,
            CategoryId = p.CategoryId,
            CategoryName = p.Category.Name,
            CreatedAt = p.CreatedAt,
            UpdatedAt = p.UpdatedAt,
            IsActive = p.IsActive,
            Photos = p.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto
            {
                Id = ph.Id,
                FileName = ph.FileName,
                FilePath = ph.FilePath,
                AltText = ph.AltText,
                SortOrder = ph.SortOrder
            }).ToList()
        }).ToListAsync();
    }

    public async Task<ProductVariationDto> CreateProductVariationAsync(CreateProductVariationDto createVariationDto)
    {
        var product = await _context.Products.FindAsync(createVariationDto.ProductId);
        if (product == null)
            throw new ArgumentException("Product not found");

        // Check if variation with this quantity already exists
        var existingVariation = await _context.ProductVariations
            .FirstOrDefaultAsync(v => v.ProductId == createVariationDto.ProductId &&
                                     v.Quantity == createVariationDto.Quantity &&
                                     v.IsActive);

        if (existingVariation != null)
            throw new ArgumentException($"A variation with quantity {createVariationDto.Quantity} already exists for this product");

        var pricePerUnit = createVariationDto.Price / createVariationDto.Quantity;

        var variation = new ProductVariation
        {
            Id = Guid.NewGuid(),
            ProductId = createVariationDto.ProductId,
            Name = createVariationDto.Name,
            Description = createVariationDto.Description,
            Quantity = createVariationDto.Quantity,
            Price = createVariationDto.Price,
            PricePerUnit = pricePerUnit,
            SortOrder = createVariationDto.SortOrder,
            IsActive = true,
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow
        };

        _context.ProductVariations.Add(variation);

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (Microsoft.EntityFrameworkCore.DbUpdateException ex) when (ex.InnerException?.Message.Contains("UNIQUE constraint failed") == true)
        {
            throw new ArgumentException($"A variation with quantity {createVariationDto.Quantity} already exists for this product");
        }

        return new ProductVariationDto
        {
            Id = variation.Id,
            ProductId = variation.ProductId,
            Name = variation.Name,
            Description = variation.Description,
            Quantity = variation.Quantity,
            Price = variation.Price,
            PricePerUnit = variation.PricePerUnit,
            SortOrder = variation.SortOrder,
            IsActive = variation.IsActive,
            CreatedAt = variation.CreatedAt,
            UpdatedAt = variation.UpdatedAt
        };
    }

    public async Task<bool> UpdateProductVariationAsync(Guid id, UpdateProductVariationDto updateVariationDto)
    {
        var variation = await _context.ProductVariations.FindAsync(id);
        if (variation == null) return false;

        if (!string.IsNullOrEmpty(updateVariationDto.Name))
            variation.Name = updateVariationDto.Name;

        if (!string.IsNullOrEmpty(updateVariationDto.Description))
            variation.Description = updateVariationDto.Description;

        if (updateVariationDto.Quantity.HasValue)
            variation.Quantity = updateVariationDto.Quantity.Value;

        if (updateVariationDto.Price.HasValue)
            variation.Price = updateVariationDto.Price.Value;

        if (updateVariationDto.Quantity.HasValue || updateVariationDto.Price.HasValue)
            variation.PricePerUnit = variation.Price / variation.Quantity;

        if (updateVariationDto.SortOrder.HasValue)
            variation.SortOrder = updateVariationDto.SortOrder.Value;

        if (updateVariationDto.IsActive.HasValue)
            variation.IsActive = updateVariationDto.IsActive.Value;

        variation.UpdatedAt = DateTime.UtcNow;

        await _context.SaveChangesAsync();
        return true;
    }

    public async Task<bool> DeleteProductVariationAsync(Guid id)
    {
        var variation = await _context.ProductVariations.FindAsync(id);
        if (variation == null) return false;

        variation.IsActive = false;
        variation.UpdatedAt = DateTime.UtcNow;
        await _context.SaveChangesAsync();
        return true;
    }

    public async Task<IEnumerable<ProductVariationDto>> GetProductVariationsAsync(Guid productId)
    {
        return await _context.ProductVariations
            .Where(v => v.ProductId == productId && v.IsActive)
            .OrderBy(v => v.SortOrder)
            .Select(v => new ProductVariationDto
            {
                Id = v.Id,
                ProductId = v.ProductId,
                Name = v.Name,
                Description = v.Description,
                Quantity = v.Quantity,
                Price = v.Price,
                PricePerUnit = v.PricePerUnit,
                SortOrder = v.SortOrder,
                IsActive = v.IsActive,
                CreatedAt = v.CreatedAt,
                UpdatedAt = v.UpdatedAt
            })
            .ToListAsync();
    }

    public async Task<ProductVariationDto?> GetProductVariationByIdAsync(Guid id)
    {
        var variation = await _context.ProductVariations.FindAsync(id);
        if (variation == null) return null;

        return new ProductVariationDto
        {
            Id = variation.Id,
            ProductId = variation.ProductId,
            Name = variation.Name,
            Description = variation.Description,
            Quantity = variation.Quantity,
            Price = variation.Price,
            PricePerUnit = variation.PricePerUnit,
            SortOrder = variation.SortOrder,
            IsActive = variation.IsActive,
            CreatedAt = variation.CreatedAt,
            UpdatedAt = variation.UpdatedAt
        };
    }
}