using MediatR;
using MktXService.Domain.AggregatesModel.TwitterAccountAggregate;
using MktXService.Domain.AggregatesModel.CampaignAggregate;
using MktXService.Domain.AggregatesModel.ConversationAggregate;
using MktXService.Domain.AggregatesModel.ContactAggregate;
using MktXService.Domain.AggregatesModel.TemplateAggregate;

namespace MktXService.API.Application.Queries;

#region Twitter Account Query Handlers

public class GetTwitterAccountsQueryHandler : IRequestHandler<GetTwitterAccountsQuery, IEnumerable<TwitterAccountDto>>
{
    private readonly ITwitterAccountRepository _repository;

    public GetTwitterAccountsQueryHandler(ITwitterAccountRepository repository) => _repository = repository;

    public async Task<IEnumerable<TwitterAccountDto>> Handle(GetTwitterAccountsQuery request, CancellationToken ct)
    {
        var accounts = await _repository.GetByMerchantIdAsync(request.MerchantId);
        return accounts.Select(a => new TwitterAccountDto(
            a.Id, a.MerchantId, a.TwitterUserId, a.Username,
            a.DisplayName, a.ProfileImageUrl, a.Status.Name, a.ConnectedAt));
    }
}

public class GetTwitterAccountQueryHandler : IRequestHandler<GetTwitterAccountQuery, TwitterAccountDto?>
{
    private readonly ITwitterAccountRepository _repository;

    public GetTwitterAccountQueryHandler(ITwitterAccountRepository repository) => _repository = repository;

    public async Task<TwitterAccountDto?> Handle(GetTwitterAccountQuery request, CancellationToken ct)
    {
        var a = await _repository.GetByIdAsync(request.AccountId);
        if (a == null) return null;
        return new TwitterAccountDto(
            a.Id, a.MerchantId, a.TwitterUserId, a.Username,
            a.DisplayName, a.ProfileImageUrl, a.Status.Name, a.ConnectedAt);
    }
}

#endregion

#region Campaign Query Handlers

public class GetCampaignsQueryHandler : IRequestHandler<GetCampaignsQuery, IEnumerable<CampaignDto>>
{
    private readonly ICampaignRepository _repository;

    public GetCampaignsQueryHandler(ICampaignRepository repository) => _repository = repository;

    public async Task<IEnumerable<CampaignDto>> Handle(GetCampaignsQuery request, CancellationToken ct)
    {
        IEnumerable<Campaign> campaigns;
        if (!string.IsNullOrEmpty(request.Status))
        {
            var status = CampaignStatus.FromName(request.Status);
            campaigns = await _repository.GetByStatusAsync(request.MerchantId, status);
        }
        else
        {
            campaigns = await _repository.GetByMerchantIdAsync(request.MerchantId, request.Skip, request.Take);
        }

        return campaigns.Select(c => new CampaignDto(
            c.Id, c.Name, c.Type, c.Status.Name,
            c.Metrics.TotalSent, c.Metrics.Delivered, c.CreatedAt));
    }
}

public class GetCampaignQueryHandler : IRequestHandler<GetCampaignQuery, CampaignDetailDto?>
{
    private readonly ICampaignRepository _repository;

    public GetCampaignQueryHandler(ICampaignRepository repository) => _repository = repository;

    public async Task<CampaignDetailDto?> Handle(GetCampaignQuery request, CancellationToken ct)
    {
        var c = await _repository.GetByIdAsync(request.CampaignId);
        if (c == null) return null;

        return new CampaignDetailDto(
            c.Id, c.Name, c.Type, c.Status.Name, c.TemplateId, c.SegmentIds.ToList(),
            new CampaignMetricsDto(c.Metrics.TotalSent, c.Metrics.Delivered, c.Metrics.Opened,
                c.Metrics.Clicked, c.Metrics.Replied, c.Metrics.Failed),
            c.Schedule != null ? new CampaignScheduleDto(c.Schedule.StartAt, c.Schedule.EndAt, c.Schedule.Recurrence) : null,
            c.CreatedAt);
    }
}

#endregion

#region Conversation Query Handlers

public class GetConversationsQueryHandler : IRequestHandler<GetConversationsQuery, IEnumerable<ConversationDto>>
{
    private readonly IConversationRepository _repository;
    private readonly IContactRepository _contactRepository;

    public GetConversationsQueryHandler(IConversationRepository repository, IContactRepository contactRepository)
    {
        _repository = repository;
        _contactRepository = contactRepository;
    }

    public async Task<IEnumerable<ConversationDto>> Handle(GetConversationsQuery request, CancellationToken ct)
    {
        IEnumerable<Conversation> conversations;

        if (request.AssignedToUserId.HasValue)
        {
            conversations = await _repository.GetByAssignedUserAsync(request.AssignedToUserId.Value, request.Skip, request.Take);
        }
        else if (!string.IsNullOrEmpty(request.Status))
        {
            var status = ConversationStatus.FromName(request.Status);
            conversations = await _repository.GetByStatusAsync(request.AccountId, status, request.Skip, request.Take);
        }
        else
        {
            conversations = await _repository.GetByAccountIdAsync(request.AccountId, request.Skip, request.Take);
        }

        var results = new List<ConversationDto>();
        foreach (var c in conversations)
        {
            var contact = await _contactRepository.GetByIdAsync(c.ContactId);
            results.Add(new ConversationDto(
                c.Id, c.ContactId, contact?.Username ?? "unknown", c.Status.Name,
                c.Messages.Count, c.StartedAt, c.Messages.Any() ? c.Messages.Max(m => m.SentAt) : null));
        }

        return results;
    }
}

public class GetConversationQueryHandler : IRequestHandler<GetConversationQuery, ConversationDetailDto?>
{
    private readonly IConversationRepository _repository;
    private readonly IContactRepository _contactRepository;

    public GetConversationQueryHandler(IConversationRepository repository, IContactRepository contactRepository)
    {
        _repository = repository;
        _contactRepository = contactRepository;
    }

    public async Task<ConversationDetailDto?> Handle(GetConversationQuery request, CancellationToken ct)
    {
        var c = request.IncludeMessages
            ? await _repository.GetByIdWithMessagesAsync(request.ConversationId)
            : await _repository.GetByIdAsync(request.ConversationId);

        if (c == null) return null;

        var contact = await _contactRepository.GetByIdAsync(c.ContactId);
        var contactDto = contact != null
            ? new ContactSummaryDto(contact.Id, contact.TwitterUserId, contact.Username,
                contact.DisplayName, contact.ProfileImageUrl)
            : new ContactSummaryDto(Guid.Empty, "", "unknown", null, null);

        return new ConversationDetailDto(
            c.Id, c.ContactId, contactDto, c.Status.Name, c.AssignedToUserId,
            c.Messages.Select(m => new MessageDto(m.Id, m.Direction, m.Type, m.Content, m.IsFromBot, m.SentAt)).ToList(),
            c.StartedAt, c.ClosedAt);
    }
}

#endregion

#region Contact Query Handlers

public class GetContactsQueryHandler : IRequestHandler<GetContactsQuery, IEnumerable<ContactDto>>
{
    private readonly IContactRepository _repository;

    public GetContactsQueryHandler(IContactRepository repository) => _repository = repository;

    public async Task<IEnumerable<ContactDto>> Handle(GetContactsQuery request, CancellationToken ct)
    {
        IEnumerable<Contact> contacts;

        if (request.Tags?.Any() == true)
        {
            contacts = await _repository.GetByTagsAsync(request.AccountId, request.Tags, request.Skip, request.Take);
        }
        else if (!string.IsNullOrEmpty(request.SearchTerm))
        {
            contacts = await _repository.SearchAsync(request.AccountId, request.SearchTerm, request.Skip, request.Take);
        }
        else
        {
            contacts = await _repository.GetByAccountIdAsync(request.AccountId, request.Skip, request.Take);
        }

        return contacts.Select(c => new ContactDto(
            c.Id, c.TwitterUserId, c.Username, c.DisplayName,
            c.Tags.Select(t => t.Name).ToList(), c.LastInteractionAt));
    }
}

public class GetContactQueryHandler : IRequestHandler<GetContactQuery, ContactDetailDto?>
{
    private readonly IContactRepository _repository;

    public GetContactQueryHandler(IContactRepository repository) => _repository = repository;

    public async Task<ContactDetailDto?> Handle(GetContactQuery request, CancellationToken ct)
    {
        var c = await _repository.GetByIdAsync(request.ContactId);
        if (c == null) return null;

        return new ContactDetailDto(
            c.Id, c.TwitterUserId, c.Username, c.DisplayName, c.ProfileImageUrl, c.Source,
            c.Attributes.ToDictionary(k => k.Key, v => v.Value),
            c.Tags.Select(t => t.Name).ToList(), c.FirstInteractionAt, c.LastInteractionAt);
    }
}

#endregion

#region Template Query Handlers

public class GetTemplatesQueryHandler : IRequestHandler<GetTemplatesQuery, IEnumerable<TemplateDto>>
{
    private readonly ITemplateRepository _repository;

    public GetTemplatesQueryHandler(ITemplateRepository repository) => _repository = repository;

    public async Task<IEnumerable<TemplateDto>> Handle(GetTemplatesQuery request, CancellationToken ct)
    {
        IEnumerable<Template> templates;

        if (!string.IsNullOrEmpty(request.Type))
        {
            templates = await _repository.GetByTypeAsync(request.MerchantId, request.Type);
        }
        else
        {
            templates = await _repository.GetByMerchantIdAsync(request.MerchantId);
        }

        return templates.Select(t => new TemplateDto(t.Id, t.Name, t.Type, t.CreatedAt));
    }
}

public class GetTemplateQueryHandler : IRequestHandler<GetTemplateQuery, TemplateDetailDto?>
{
    private readonly ITemplateRepository _repository;

    public GetTemplateQueryHandler(ITemplateRepository repository) => _repository = repository;

    public async Task<TemplateDetailDto?> Handle(GetTemplateQuery request, CancellationToken ct)
    {
        var t = await _repository.GetByIdAsync(request.TemplateId);
        if (t == null) return null;

        return new TemplateDetailDto(t.Id, t.Name, t.Type, t.Content, t.Variables.ToList(), t.CreatedAt);
    }
}

#endregion
