using MemberCenter.Application.Abstractions;
using MemberCenter.Application.Constants;
using MemberCenter.Domain.Constants;
using MemberCenter.Infrastructure.Identity;
using MemberCenter.Infrastructure.Persistence;
using MemberCenter.Web.Models.TenantNewsletters;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace MemberCenter.Web.Controllers;

[Authorize(Roles = AdminPermissions.TenantManagerRole)]
[Route("tenant/newsletters")]
public sealed class TenantNewslettersController : Controller
{
    private readonly ITenantManagerService _tenantManagerService;
    private readonly MemberCenterDbContext _dbContext;
    private readonly UserManager<ApplicationUser> _userManager;

    public TenantNewslettersController(
        ITenantManagerService tenantManagerService,
        MemberCenterDbContext dbContext,
        UserManager<ApplicationUser> userManager)
    {
        _tenantManagerService = tenantManagerService;
        _dbContext = dbContext;
        _userManager = userManager;
    }

    [HttpGet("")]
    public async Task<IActionResult> Index(Guid? tenantId = null)
    {
        var user = await _userManager.GetUserAsync(User);
        if (user is null)
        {
            return RedirectToAction("Login", "Account", new { area = string.Empty });
        }

        var tenantIds = await _tenantManagerService.ListTenantIdsForUserAsync(user.Id);
        if (tenantIds.Count == 0)
        {
            return View(new TenantNewsletterManagementViewModel());
        }

        if (tenantId.HasValue && !tenantIds.Contains(tenantId.Value))
        {
            return NotFound();
        }

        var managedTenantIds = tenantIds.ToList();
        var tenantOptions = await _dbContext.Tenants
            .AsNoTracking()
            .Where(tenant => managedTenantIds.Contains(tenant.Id))
            .OrderBy(tenant => tenant.Name)
            .Select(tenant => new TenantNewsletterTenantOptionViewModel
            {
                Id = tenant.Id,
                Name = tenant.Name
            })
            .ToListAsync();

        var visibleTenantIds = tenantId.HasValue
            ? new HashSet<Guid> { tenantId.Value }
            : tenantIds.ToHashSet();

        var visibleTenantIdList = visibleTenantIds.ToList();
        var newsletters = await _dbContext.NewsletterLists
            .AsNoTracking()
            .Include(list => list.Tenant)
            .Where(list => visibleTenantIdList.Contains(list.TenantId))
            .OrderBy(list => list.Tenant!.Name)
            .ThenBy(list => list.Name)
            .ToListAsync();
        var visibleListIds = newsletters.Select(list => list.Id).ToList();

        var counts = await _dbContext.NewsletterSubscriptions
            .AsNoTracking()
            .Where(subscription => visibleListIds.Contains(subscription.ListId))
            .GroupBy(subscription => new { subscription.ListId, subscription.Status })
            .Select(group => new
            {
                group.Key.ListId,
                group.Key.Status,
                Count = group.Count()
            })
            .ToListAsync();

        var activeCounts = counts
            .Where(item => item.Status == SubscriptionStatus.Active)
            .ToDictionary(item => item.ListId, item => item.Count);
        var unsubscribedCounts = counts
            .Where(item => item.Status == SubscriptionStatus.Unsubscribed)
            .ToDictionary(item => item.ListId, item => item.Count);

        var groups = newsletters
            .GroupBy(list => new
            {
                list.TenantId,
                TenantName = list.Tenant?.Name ?? string.Empty
            })
            .Select(group => new TenantNewsletterGroupViewModel
            {
                TenantId = group.Key.TenantId,
                TenantName = group.Key.TenantName,
                Newsletters = group.Select(list => new TenantNewsletterCardViewModel
                {
                    Id = list.Id,
                    Name = list.Name,
                    TenantName = group.Key.TenantName,
                    ActiveSubscriptionCount = activeCounts.TryGetValue(list.Id, out var activeCount) ? activeCount : 0,
                    UnsubscribedCount = unsubscribedCounts.TryGetValue(list.Id, out var unsubscribedCount) ? unsubscribedCount : 0
                }).ToList()
            })
            .ToList();

        return View(new TenantNewsletterManagementViewModel
        {
            SelectedTenantId = tenantId,
            Tenants = tenantOptions,
            Groups = groups
        });
    }

    [HttpGet("{id:guid}")]
    public async Task<IActionResult> Details(Guid id, string? tab = null, string? search = null)
    {
        var user = await _userManager.GetUserAsync(User);
        if (user is null)
        {
            return RedirectToAction("Login", "Account", new { area = string.Empty });
        }

        var tenantIds = await _tenantManagerService.ListTenantIdsForUserAsync(user.Id);
        if (tenantIds.Count == 0)
        {
            return NotFound();
        }

        var managedTenantIds = tenantIds.ToList();
        var list = await _dbContext.NewsletterLists
            .AsNoTracking()
            .Include(item => item.Tenant)
            .FirstOrDefaultAsync(item => item.Id == id && managedTenantIds.Contains(item.TenantId));
        if (list is null)
        {
            return NotFound();
        }

        var activeCount = await _dbContext.NewsletterSubscriptions
            .AsNoTracking()
            .CountAsync(subscription => subscription.ListId == id && subscription.Status == SubscriptionStatus.Active);
        var unsubscribedCount = await _dbContext.NewsletterSubscriptions
            .AsNoTracking()
            .CountAsync(subscription => subscription.ListId == id && subscription.Status == SubscriptionStatus.Unsubscribed);

        var normalizedTab = NormalizeTab(tab);
        var normalizedSearch = string.IsNullOrWhiteSpace(search) ? null : search.Trim();
        var rows = normalizedTab == "blacklisted"
            ? await BuildBlacklistedRowsAsync(id, normalizedSearch)
            : await BuildSubscriptionRowsAsync(id, MapTabToStatus(normalizedTab), normalizedSearch);

        return View(new TenantNewsletterDetailViewModel
        {
            Id = list.Id,
            TenantId = list.TenantId,
            TenantName = list.Tenant?.Name ?? string.Empty,
            Name = list.Name,
            Tab = normalizedTab,
            Search = normalizedSearch,
            ActiveSubscriptionCount = activeCount,
            UnsubscribedCount = unsubscribedCount,
            Rows = rows
        });
    }

    private async Task<IReadOnlyList<TenantNewsletterSubscriptionRowViewModel>> BuildSubscriptionRowsAsync(
        Guid listId,
        string status,
        string? search)
    {
        var query = _dbContext.NewsletterSubscriptions
            .AsNoTracking()
            .Where(subscription => subscription.ListId == listId && subscription.Status == status);

        if (!string.IsNullOrWhiteSpace(search))
        {
            var keyword = search.Trim().ToLowerInvariant();
            query = query.Where(subscription => subscription.Email.ToLower().Contains(keyword));
        }

        return await query
            .OrderByDescending(subscription => subscription.CreatedAt)
            .Select(subscription => new TenantNewsletterSubscriptionRowViewModel
            {
                Id = subscription.Id,
                Email = subscription.Email,
                ListId = subscription.ListId.ToString(),
                CreatedAt = subscription.CreatedAt
            })
            .ToListAsync();
    }

    private async Task<IReadOnlyList<TenantNewsletterSubscriptionRowViewModel>> BuildBlacklistedRowsAsync(
        Guid listId,
        string? search)
    {
        var blacklistedEmails = _dbContext.EmailBlacklist.Select(item => item.Email);
        var query = _dbContext.NewsletterSubscriptions
            .AsNoTracking()
            .Where(subscription => subscription.ListId == listId
                && blacklistedEmails.Contains(subscription.Email.ToLower()));

        if (!string.IsNullOrWhiteSpace(search))
        {
            var keyword = search.Trim().ToLowerInvariant();
            query = query.Where(subscription => subscription.Email.ToLower().Contains(keyword));
        }

        return await query
            .OrderByDescending(subscription => subscription.CreatedAt)
            .Select(subscription => new TenantNewsletterSubscriptionRowViewModel
            {
                Id = subscription.Id,
                Email = subscription.Email,
                ListId = subscription.ListId.ToString(),
                CreatedAt = subscription.CreatedAt,
                Reason = _dbContext.EmailBlacklist
                    .Where(item => item.Email == subscription.Email.ToLower())
                    .Select(item => item.Reason)
                    .FirstOrDefault()
            })
            .ToListAsync();
    }

    private static string NormalizeTab(string? tab) =>
        tab?.ToLowerInvariant() switch
        {
            "unsubscribed" => "unsubscribed",
            "pending" => "pending",
            "blacklisted" => "blacklisted",
            _ => "subscribers"
        };

    private static string MapTabToStatus(string tab) =>
        tab switch
        {
            "unsubscribed" => SubscriptionStatus.Unsubscribed,
            "pending" => SubscriptionStatus.Pending,
            _ => SubscriptionStatus.Active
        };
}
