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

namespace LittleShop.Services;

public class OrderService : IOrderService
{
    private readonly LittleShopContext _context;
    private readonly ILogger<OrderService> _logger;
    private readonly ICustomerService _customerService;

    public OrderService(LittleShopContext context, ILogger<OrderService> logger, ICustomerService customerService)
    {
        _context = context;
        _logger = logger;
        _customerService = customerService;
    }

    public async Task<IEnumerable<OrderDto>> GetAllOrdersAsync()
    {
        var orders = await _context.Orders
            .Include(o => o.Customer)
            .Include(o => o.Items)
            .ThenInclude(oi => oi.Product)
            .Include(o => o.Payments)
            .OrderByDescending(o => o.CreatedAt)
            .ToListAsync();

        return orders.Select(MapToDto);
    }

    public async Task<IEnumerable<OrderDto>> GetOrdersByIdentityAsync(string identityReference)
    {
        var orders = await _context.Orders
            .Include(o => o.Customer)
            .Include(o => o.Items)
            .ThenInclude(oi => oi.Product)
            .Include(o => o.Payments)
            .Where(o => o.IdentityReference == identityReference)
            .OrderByDescending(o => o.CreatedAt)
            .ToListAsync();

        return orders.Select(MapToDto);
    }

    public async Task<IEnumerable<OrderDto>> GetOrdersByCustomerIdAsync(Guid customerId)
    {
        var orders = await _context.Orders
            .Include(o => o.Customer)
            .Include(o => o.Items)
            .ThenInclude(oi => oi.Product)
            .Include(o => o.Payments)
            .Where(o => o.CustomerId == customerId)
            .OrderByDescending(o => o.CreatedAt)
            .ToListAsync();

        return orders.Select(MapToDto);
    }

    public async Task<OrderDto?> GetOrderByIdAsync(Guid id)
    {
        var order = await _context.Orders
            .Include(o => o.Customer)
            .Include(o => o.Items)
            .ThenInclude(oi => oi.Product)
            .Include(o => o.Payments)
            .FirstOrDefaultAsync(o => o.Id == id);

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

    public async Task<OrderDto> CreateOrderAsync(CreateOrderDto createOrderDto)
    {
        using var transaction = await _context.Database.BeginTransactionAsync();

        try
        {
            // Handle customer creation/linking during checkout
            Guid? customerId = null;
            string? identityReference = null;
            
            if (createOrderDto.CustomerInfo != null)
            {
                // Create customer during checkout process
                var customer = await _customerService.GetOrCreateCustomerAsync(
                    createOrderDto.CustomerInfo.TelegramUserId,
                    createOrderDto.CustomerInfo.TelegramDisplayName,
                    createOrderDto.CustomerInfo.TelegramUsername,
                    createOrderDto.CustomerInfo.TelegramFirstName,
                    createOrderDto.CustomerInfo.TelegramLastName);
                    
                customerId = customer?.Id;
            }
            else if (createOrderDto.CustomerId.HasValue)
            {
                // Order for existing customer
                customerId = createOrderDto.CustomerId;
            }
            else
            {
                // Anonymous order (legacy support)
                identityReference = createOrderDto.IdentityReference;
            }

            var order = new Order
            {
                Id = Guid.NewGuid(),
                CustomerId = customerId,
                IdentityReference = identityReference,
                Status = OrderStatus.PendingPayment,
                TotalAmount = 0,
                Currency = "GBP",
                ShippingName = createOrderDto.ShippingName,
                ShippingAddress = createOrderDto.ShippingAddress,
                ShippingCity = createOrderDto.ShippingCity,
                ShippingPostCode = createOrderDto.ShippingPostCode,
                ShippingCountry = createOrderDto.ShippingCountry,
                Notes = createOrderDto.Notes,
                CreatedAt = DateTime.UtcNow,
                UpdatedAt = DateTime.UtcNow
            };

            _context.Orders.Add(order);

            decimal totalAmount = 0;
            foreach (var itemDto in createOrderDto.Items)
            {
                var product = await _context.Products.FindAsync(itemDto.ProductId);
                if (product == null || !product.IsActive)
                {
                    throw new ArgumentException($"Product {itemDto.ProductId} not found or inactive");
                }

                var orderItem = new OrderItem
                {
                    Id = Guid.NewGuid(),
                    OrderId = order.Id,
                    ProductId = itemDto.ProductId,
                    Quantity = itemDto.Quantity,
                    UnitPrice = product.Price,
                    TotalPrice = product.Price * itemDto.Quantity
                };

                _context.OrderItems.Add(orderItem);
                totalAmount += orderItem.TotalPrice;
            }

            order.TotalAmount = totalAmount;
            await _context.SaveChangesAsync();
            await transaction.CommitAsync();

            if (customerId.HasValue)
            {
                _logger.LogInformation("Created order {OrderId} for customer {CustomerId} with total {Total}", 
                    order.Id, customerId.Value, totalAmount);
            }
            else
            {
                _logger.LogInformation("Created order {OrderId} for identity {Identity} with total {Total}", 
                    order.Id, identityReference, totalAmount);
            }

            // Reload order with includes
            var createdOrder = await GetOrderByIdAsync(order.Id);
            return createdOrder!;
        }
        catch
        {
            await transaction.RollbackAsync();
            throw;
        }
    }

    public async Task<bool> UpdateOrderStatusAsync(Guid id, UpdateOrderStatusDto updateOrderStatusDto)
    {
        var order = await _context.Orders.FindAsync(id);
        if (order == null) return false;

        order.Status = updateOrderStatusDto.Status;
        
        if (!string.IsNullOrEmpty(updateOrderStatusDto.TrackingNumber))
        {
            order.TrackingNumber = updateOrderStatusDto.TrackingNumber;
        }

        if (!string.IsNullOrEmpty(updateOrderStatusDto.Notes))
        {
            order.Notes = updateOrderStatusDto.Notes;
        }

        if (updateOrderStatusDto.Status == OrderStatus.Shipped && order.ShippedAt == null)
        {
            order.ShippedAt = DateTime.UtcNow;
        }

        order.UpdatedAt = DateTime.UtcNow;

        await _context.SaveChangesAsync();

        _logger.LogInformation("Updated order {OrderId} status to {Status}", id, updateOrderStatusDto.Status);

        return true;
    }

    public async Task<bool> CancelOrderAsync(Guid id, string identityReference)
    {
        var order = await _context.Orders.FindAsync(id);
        if (order == null || order.IdentityReference != identityReference) 
            return false;

        if (order.Status != OrderStatus.PendingPayment)
        {
            return false; // Can only cancel pending orders
        }

        order.Status = OrderStatus.Cancelled;
        order.UpdatedAt = DateTime.UtcNow;
        await _context.SaveChangesAsync();

        _logger.LogInformation("Cancelled order {OrderId} by identity {Identity}", id, identityReference);

        return true;
    }

    private static OrderDto MapToDto(Order order)
    {
        return new OrderDto
        {
            Id = order.Id,
            CustomerId = order.CustomerId,
            IdentityReference = order.IdentityReference,
            Status = order.Status,
            Customer = order.Customer != null ? new CustomerSummaryDto
            {
                Id = order.Customer.Id,
                DisplayName = !string.IsNullOrEmpty(order.Customer.TelegramDisplayName) ? order.Customer.TelegramDisplayName :
                             !string.IsNullOrEmpty(order.Customer.TelegramUsername) ? $"@{order.Customer.TelegramUsername}" :
                             $"{order.Customer.TelegramFirstName} {order.Customer.TelegramLastName}".Trim(),
                TelegramUsername = order.Customer.TelegramUsername,
                TotalOrders = order.Customer.TotalOrders,
                TotalSpent = order.Customer.TotalSpent,
                CustomerType = order.Customer.TotalOrders == 0 ? "New" :
                              order.Customer.TotalOrders == 1 ? "First-time" :
                              order.Customer.TotalOrders < 5 ? "Regular" :
                              order.Customer.TotalOrders < 20 ? "Loyal" : "VIP",
                RiskScore = order.Customer.RiskScore,
                LastActiveAt = order.Customer.LastActiveAt,
                IsBlocked = order.Customer.IsBlocked
            } : null,
            TotalAmount = order.TotalAmount,
            Currency = order.Currency,
            ShippingName = order.ShippingName,
            ShippingAddress = order.ShippingAddress,
            ShippingCity = order.ShippingCity,
            ShippingPostCode = order.ShippingPostCode,
            ShippingCountry = order.ShippingCountry,
            Notes = order.Notes,
            TrackingNumber = order.TrackingNumber,
            CreatedAt = order.CreatedAt,
            UpdatedAt = order.UpdatedAt,
            PaidAt = order.PaidAt,
            ShippedAt = order.ShippedAt,
            Items = order.Items.Select(oi => new OrderItemDto
            {
                Id = oi.Id,
                ProductId = oi.ProductId,
                ProductName = oi.Product.Name,
                Quantity = oi.Quantity,
                UnitPrice = oi.UnitPrice,
                TotalPrice = oi.TotalPrice
            }).ToList(),
            Payments = order.Payments.Select(cp => new CryptoPaymentDto
            {
                Id = cp.Id,
                OrderId = cp.OrderId,
                Currency = cp.Currency,
                WalletAddress = cp.WalletAddress,
                RequiredAmount = cp.RequiredAmount,
                PaidAmount = cp.PaidAmount,
                Status = cp.Status,
                BTCPayInvoiceId = cp.BTCPayInvoiceId,
                TransactionHash = cp.TransactionHash,
                CreatedAt = cp.CreatedAt,
                PaidAt = cp.PaidAt,
                ExpiresAt = cp.ExpiresAt
            }).ToList()
        };
    }
}