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

namespace LittleShop.Services;

public interface IReviewService
{
    Task<ReviewDto?> GetReviewByIdAsync(Guid id);
    Task<IEnumerable<ReviewDto>> GetReviewsByProductAsync(Guid productId, bool approvedOnly = true);
    Task<IEnumerable<ReviewDto>> GetReviewsByCustomerAsync(Guid customerId);
    Task<IEnumerable<ReviewDto>> GetPendingReviewsAsync();
    Task<ReviewSummaryDto?> GetProductReviewSummaryAsync(Guid productId);
    Task<CustomerReviewEligibilityDto> CheckReviewEligibilityAsync(Guid customerId, Guid productId);
    Task<ReviewDto> CreateReviewAsync(CreateReviewDto createReviewDto);
    Task<bool> UpdateReviewAsync(Guid id, UpdateReviewDto updateReviewDto);
    Task<bool> ApproveReviewAsync(Guid id, Guid approvedByUserId);
    Task<bool> DeleteReviewAsync(Guid id);
    Task<bool> CanCustomerReviewProductAsync(Guid customerId, Guid productId);
}

public class ReviewService : IReviewService
{
    private readonly LittleShopContext _context;
    private readonly ILogger<ReviewService> _logger;

    public ReviewService(LittleShopContext context, ILogger<ReviewService> logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task<ReviewDto?> GetReviewByIdAsync(Guid id)
    {
        var review = await _context.Reviews
            .Include(r => r.Product)
            .Include(r => r.Customer)
            .Include(r => r.ApprovedByUser)
            .FirstOrDefaultAsync(r => r.Id == id);

        return review == null ? null : MapToDto(review);
    }

    public async Task<IEnumerable<ReviewDto>> GetReviewsByProductAsync(Guid productId, bool approvedOnly = true)
    {
        var query = _context.Reviews
            .Include(r => r.Customer)
            .Include(r => r.ApprovedByUser)
            .Where(r => r.ProductId == productId && r.IsActive);

        if (approvedOnly)
        {
            query = query.Where(r => r.IsApproved);
        }

        var reviews = await query
            .OrderByDescending(r => r.CreatedAt)
            .ToListAsync();

        return reviews.Select(MapToDto);
    }

    public async Task<IEnumerable<ReviewDto>> GetReviewsByCustomerAsync(Guid customerId)
    {
        var reviews = await _context.Reviews
            .Include(r => r.Product)
            .Include(r => r.ApprovedByUser)
            .Where(r => r.CustomerId == customerId && r.IsActive)
            .OrderByDescending(r => r.CreatedAt)
            .ToListAsync();

        return reviews.Select(MapToDto);
    }

    public async Task<IEnumerable<ReviewDto>> GetPendingReviewsAsync()
    {
        var reviews = await _context.Reviews
            .Include(r => r.Product)
            .Include(r => r.Customer)
            .Where(r => !r.IsApproved && r.IsActive)
            .OrderBy(r => r.CreatedAt)
            .ToListAsync();

        return reviews.Select(MapToDto);
    }

    public async Task<ReviewSummaryDto?> GetProductReviewSummaryAsync(Guid productId)
    {
        var product = await _context.Products
            .Include(p => p.Reviews.Where(r => r.IsApproved && r.IsActive))
            .FirstOrDefaultAsync(p => p.Id == productId);

        if (product == null) return null;

        var approvedReviews = product.Reviews.Where(r => r.IsApproved && r.IsActive).ToList();
        
        if (!approvedReviews.Any())
        {
            return new ReviewSummaryDto
            {
                ProductId = productId,
                ProductName = product.Name,
                TotalReviews = 0,
                ApprovedReviews = 0,
                AverageRating = 0
            };
        }

        return new ReviewSummaryDto
        {
            ProductId = productId,
            ProductName = product.Name,
            TotalReviews = approvedReviews.Count,
            ApprovedReviews = approvedReviews.Count,
            AverageRating = Math.Round(approvedReviews.Average(r => r.Rating), 1),
            FiveStars = approvedReviews.Count(r => r.Rating == 5),
            FourStars = approvedReviews.Count(r => r.Rating == 4),
            ThreeStars = approvedReviews.Count(r => r.Rating == 3),
            TwoStars = approvedReviews.Count(r => r.Rating == 2),
            OneStar = approvedReviews.Count(r => r.Rating == 1),
            LatestReviewDate = approvedReviews.Max(r => r.CreatedAt)
        };
    }

    public async Task<CustomerReviewEligibilityDto> CheckReviewEligibilityAsync(Guid customerId, Guid productId)
    {
        // Check if customer has already reviewed this product
        var existingReview = await _context.Reviews
            .FirstOrDefaultAsync(r => r.CustomerId == customerId && r.ProductId == productId && r.IsActive);

        // Get shipped orders containing this product for this customer
        var eligibleOrders = await _context.Orders
            .Include(o => o.Items)
            .Where(o => o.CustomerId == customerId 
                       && o.Status == OrderStatus.Shipped 
                       && o.Items.Any(oi => oi.ProductId == productId))
            .Select(o => o.Id)
            .ToListAsync();

        var canReview = eligibleOrders.Any() && existingReview == null;
        var reason = !eligibleOrders.Any() 
            ? "You must have a shipped order containing this product to leave a review"
            : existingReview != null 
                ? "You have already reviewed this product"
                : null;

        return new CustomerReviewEligibilityDto
        {
            CustomerId = customerId,
            ProductId = productId,
            CanReview = canReview,
            Reason = reason,
            EligibleOrderIds = eligibleOrders,
            HasExistingReview = existingReview != null,
            ExistingReviewId = existingReview?.Id
        };
    }

    public async Task<ReviewDto> CreateReviewAsync(CreateReviewDto createReviewDto)
    {
        // Verify customer can review this product
        var eligibility = await CheckReviewEligibilityAsync(createReviewDto.CustomerId, createReviewDto.ProductId);
        if (!eligibility.CanReview)
        {
            throw new InvalidOperationException(eligibility.Reason ?? "Cannot create review");
        }

        // Verify the order exists and contains the product
        var order = await _context.Orders
            .Include(o => o.Items)
            .FirstOrDefaultAsync(o => o.Id == createReviewDto.OrderId 
                                   && o.CustomerId == createReviewDto.CustomerId
                                   && o.Status == OrderStatus.Shipped
                                   && o.Items.Any(oi => oi.ProductId == createReviewDto.ProductId));

        if (order == null)
        {
            throw new InvalidOperationException("Invalid order or product not found in shipped order");
        }

        var review = new Review
        {
            Id = Guid.NewGuid(),
            ProductId = createReviewDto.ProductId,
            CustomerId = createReviewDto.CustomerId,
            OrderId = createReviewDto.OrderId,
            Rating = createReviewDto.Rating,
            Title = createReviewDto.Title,
            Comment = createReviewDto.Comment,
            IsVerifiedPurchase = true,
            IsApproved = false, // Reviews require admin approval
            IsActive = true,
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow
        };

        _context.Reviews.Add(review);
        await _context.SaveChangesAsync();

        _logger.LogInformation("Review created: {ReviewId} for product {ProductId} by customer {CustomerId}", 
            review.Id, createReviewDto.ProductId, createReviewDto.CustomerId);

        // Load navigation properties for return DTO
        review = await _context.Reviews
            .Include(r => r.Product)
            .Include(r => r.Customer)
            .FirstAsync(r => r.Id == review.Id);

        return MapToDto(review);
    }

    public async Task<bool> UpdateReviewAsync(Guid id, UpdateReviewDto updateReviewDto)
    {
        var review = await _context.Reviews.FindAsync(id);
        if (review == null) return false;

        if (updateReviewDto.Rating.HasValue)
            review.Rating = updateReviewDto.Rating.Value;
        
        if (updateReviewDto.Title != null)
            review.Title = updateReviewDto.Title;
        
        if (updateReviewDto.Comment != null)
            review.Comment = updateReviewDto.Comment;
        
        if (updateReviewDto.IsApproved.HasValue)
            review.IsApproved = updateReviewDto.IsApproved.Value;
        
        if (updateReviewDto.IsActive.HasValue)
            review.IsActive = updateReviewDto.IsActive.Value;

        review.UpdatedAt = DateTime.UtcNow;

        await _context.SaveChangesAsync();
        
        _logger.LogInformation("Review updated: {ReviewId}", id);
        return true;
    }

    public async Task<bool> ApproveReviewAsync(Guid id, Guid approvedByUserId)
    {
        var review = await _context.Reviews.FindAsync(id);
        if (review == null) return false;

        review.IsApproved = true;
        review.ApprovedAt = DateTime.UtcNow;
        review.ApprovedByUserId = approvedByUserId;
        review.UpdatedAt = DateTime.UtcNow;

        await _context.SaveChangesAsync();
        
        _logger.LogInformation("Review approved: {ReviewId} by user {UserId}", id, approvedByUserId);
        return true;
    }

    public async Task<bool> DeleteReviewAsync(Guid id)
    {
        var review = await _context.Reviews.FindAsync(id);
        if (review == null) return false;

        review.IsActive = false;
        review.UpdatedAt = DateTime.UtcNow;

        await _context.SaveChangesAsync();
        
        _logger.LogInformation("Review soft deleted: {ReviewId}", id);
        return true;
    }

    public async Task<bool> CanCustomerReviewProductAsync(Guid customerId, Guid productId)
    {
        var eligibility = await CheckReviewEligibilityAsync(customerId, productId);
        return eligibility.CanReview;
    }

    private static ReviewDto MapToDto(Review review)
    {
        return new ReviewDto
        {
            Id = review.Id,
            ProductId = review.ProductId,
            ProductName = review.Product?.Name ?? "",
            CustomerId = review.CustomerId,
            CustomerDisplayName = review.Customer?.TelegramDisplayName ?? "Anonymous",
            OrderId = review.OrderId,
            Rating = review.Rating,
            Title = review.Title,
            Comment = review.Comment,
            IsVerifiedPurchase = review.IsVerifiedPurchase,
            IsApproved = review.IsApproved,
            IsActive = review.IsActive,
            CreatedAt = review.CreatedAt,
            UpdatedAt = review.UpdatedAt,
            ApprovedAt = review.ApprovedAt,
            ApprovedByUsername = review.ApprovedByUser?.Username
        };
    }
}