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

namespace LittleShop.Services;

public class CustomerMessageService : ICustomerMessageService
{
    private readonly LittleShopContext _context;
    private readonly IMapper _mapper;
    private readonly ILogger<CustomerMessageService> _logger;

    public CustomerMessageService(LittleShopContext context, IMapper mapper, ILogger<CustomerMessageService> logger)
    {
        _context = context;
        _mapper = mapper;
        _logger = logger;
    }

    public async Task<CustomerMessageDto?> CreateMessageAsync(CreateCustomerMessageDto createMessageDto)
    {
        try
        {
            var message = _mapper.Map<CustomerMessage>(createMessageDto);
            message.Id = Guid.NewGuid();
            message.Direction = MessageDirection.AdminToCustomer;
            message.CreatedAt = DateTime.UtcNow;
            message.Status = MessageStatus.Pending;
            message.Platform = "Telegram";

            // Use customer-based threading - all messages for a customer are in one conversation
            message.ThreadId = message.CustomerId;

            _context.CustomerMessages.Add(message);
            await _context.SaveChangesAsync();

            _logger.LogInformation("Created message {MessageId} for customer {CustomerId}", 
                message.Id, message.CustomerId);

            // Return the created message with includes
            var createdMessage = await GetMessageByIdAsync(message.Id);
            return createdMessage;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating message for customer {CustomerId}", createMessageDto.CustomerId);
            return null;
        }
    }

    public async Task<CustomerMessageDto?> GetMessageByIdAsync(Guid id)
    {
        var message = await _context.CustomerMessages
            .Include(m => m.Customer)
            .Include(m => m.Order)
            .Include(m => m.AdminUser)
            .Include(m => m.ParentMessage)
            .Include(m => m.Replies)
            .FirstOrDefaultAsync(m => m.Id == id);

        if (message == null) return null;

        return _mapper.Map<CustomerMessageDto>(message);
    }

    public async Task<IEnumerable<CustomerMessageDto>> GetCustomerMessagesAsync(Guid customerId)
    {
        var messages = await _context.CustomerMessages
            .Include(m => m.Customer)
            .Include(m => m.Order)
            .Include(m => m.AdminUser)
            .Where(m => m.CustomerId == customerId && !m.IsArchived)
            .OrderByDescending(m => m.CreatedAt)
            .ToListAsync();

        return messages.Select(m => _mapper.Map<CustomerMessageDto>(m));
    }

    public async Task<IEnumerable<CustomerMessageDto>> GetOrderMessagesAsync(Guid orderId)
    {
        var messages = await _context.CustomerMessages
            .Include(m => m.Customer)
            .Include(m => m.Order)
            .Include(m => m.AdminUser)
            .Where(m => m.OrderId == orderId && !m.IsArchived)
            .OrderByDescending(m => m.CreatedAt)
            .ToListAsync();

        return messages.Select(m => _mapper.Map<CustomerMessageDto>(m));
    }

    public async Task<IEnumerable<CustomerMessageDto>> GetPendingMessagesAsync(string platform = "Telegram")
    {
        var messages = await _context.CustomerMessages
            .Include(m => m.Customer)
            .Include(m => m.Order)
            .Include(m => m.AdminUser)
            .Where(m => m.Status == MessageStatus.Pending && 
                       m.Platform == platform &&
                       m.Direction == MessageDirection.AdminToCustomer &&
                       (m.ScheduledFor == null || m.ScheduledFor <= DateTime.UtcNow) &&
                       (m.ExpiresAt == null || m.ExpiresAt > DateTime.UtcNow))
            .OrderBy(m => m.Priority)
            .ThenBy(m => m.CreatedAt)
            .Take(50) // Limit for performance
            .ToListAsync();

        return messages.Select(m => _mapper.Map<CustomerMessageDto>(m));
    }

    public async Task<bool> MarkMessageAsSentAsync(Guid messageId, string? platformMessageId = null)
    {
        var message = await _context.CustomerMessages.FindAsync(messageId);
        if (message == null) return false;

        message.MarkAsSent();
        if (!string.IsNullOrEmpty(platformMessageId))
        {
            message.PlatformMessageId = platformMessageId;
        }

        await _context.SaveChangesAsync();

        _logger.LogInformation("Marked message {MessageId} as sent", messageId);
        return true;
    }

    public async Task<bool> MarkMessageAsDeliveredAsync(Guid messageId)
    {
        var message = await _context.CustomerMessages.FindAsync(messageId);
        if (message == null) return false;

        message.MarkAsDelivered();
        await _context.SaveChangesAsync();

        _logger.LogInformation("Marked message {MessageId} as delivered", messageId);
        return true;
    }

    public async Task<bool> MarkMessageAsFailedAsync(Guid messageId, string reason)
    {
        var message = await _context.CustomerMessages.FindAsync(messageId);
        if (message == null) return false;

        message.MarkAsFailed(reason);
        await _context.SaveChangesAsync();

        _logger.LogWarning("Marked message {MessageId} as failed: {Reason}", messageId, reason);
        return true;
    }

    public async Task<MessageThreadDto?> GetMessageThreadAsync(Guid customerId)
    {
        // Get all messages for this customer (customer-based conversation)
        var messages = await _context.CustomerMessages
            .Include(m => m.Customer)
            .Include(m => m.Order)
            .Include(m => m.AdminUser)
            .Where(m => m.CustomerId == customerId && !m.IsArchived)
            .OrderBy(m => m.CreatedAt)
            .ToListAsync();

        if (!messages.Any()) return null;

        var customer = messages.First().Customer;
        var thread = new MessageThreadDto
        {
            ThreadId = customerId, // Use CustomerId as thread identifier
            Subject = customer?.DisplayName ?? "Unknown Customer", // Customer name as subject
            CustomerId = customerId,
            CustomerName = customer?.DisplayName ?? "Unknown",
            OrderId = null, // No single order - conversation spans multiple orders
            OrderReference = null, // No single order reference
            StartedAt = messages.Min(m => m.CreatedAt),
            LastMessageAt = messages.Max(m => m.CreatedAt),
            MessageCount = messages.Count,
            HasUnreadMessages = messages.Any(m => m.Direction == MessageDirection.CustomerToAdmin && m.Status != MessageStatus.Read),
            RequiresResponse = messages.Any(m => m.RequiresResponse && m.Status != MessageStatus.Read),
            Messages = messages.Select(m => _mapper.Map<CustomerMessageDto>(m)).ToList()
        };

        return thread;
    }

    public async Task<IEnumerable<MessageThreadDto>> GetActiveThreadsAsync()
    {
        // Group by Customer instead of ThreadId for customer-based conversations
        var threads = await _context.CustomerMessages
            .Include(m => m.Customer)
            .Include(m => m.Order)
            .Where(m => !m.IsArchived)
            .GroupBy(m => m.CustomerId)
            .Select(g => new MessageThreadDto
            {
                ThreadId = g.First().CustomerId, // Use CustomerId as conversation identifier
                Subject = g.First().Customer != null ? g.First().Customer.DisplayName : "Unknown Customer",
                CustomerId = g.First().CustomerId,
                CustomerName = g.First().Customer != null ? g.First().Customer.DisplayName : "Unknown",
                OrderId = null, // No single order - will show multiple orders in thread
                OrderReference = null, // No single order reference
                StartedAt = g.Min(m => m.CreatedAt),
                LastMessageAt = g.Max(m => m.CreatedAt),
                MessageCount = g.Count(),
                HasUnreadMessages = g.Any(m => m.Direction == MessageDirection.CustomerToAdmin && m.Status != MessageStatus.Read),
                RequiresResponse = g.Any(m => m.RequiresResponse && m.Status != MessageStatus.Read)
            })
            .OrderByDescending(t => t.LastMessageAt)
            .Take(100) // Limit for performance
            .ToListAsync();

        return threads;
    }

    public async Task<bool> ValidateCustomerExistsAsync(Guid customerId)
    {
        return await _context.Customers.AnyAsync(c => c.Id == customerId);
    }

    public async Task<bool> ValidateOrderBelongsToCustomerAsync(Guid orderId, Guid customerId)
    {
        return await _context.Orders.AnyAsync(o => o.Id == orderId && o.CustomerId == customerId);
    }

    public async Task<bool> CreateCustomerToAdminMessageAsync(CustomerMessage message)
    {
        try
        {
            // Use customer-based threading for all messages
            message.ThreadId = message.CustomerId;
            
            _context.CustomerMessages.Add(message);
            await _context.SaveChangesAsync();

            _logger.LogInformation("Created customer message {MessageId} from customer {CustomerId}", 
                message.Id, message.CustomerId);

            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating customer message from customer {CustomerId}", message.CustomerId);
            return false;
        }
    }
}