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

namespace MemberCenter.Web.Areas.Admin.Controllers;

[Area("Admin")]
[Authorize(Policy = "Admin")]
[AdminPermission(AdminPermissions.Tenants)]
[Route("admin/tenants")]
public class TenantsController : Controller
{
    private readonly ITenantService _tenantService;
    private readonly ITenantManagerService _tenantManagerService;
    private readonly MemberCenterDbContext _dbContext;
    private readonly UserManager<ApplicationUser> _userManager;

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

    [HttpGet("")]
    [AdminPermission(AdminPermissions.TenantsIndex)]
    public async Task<IActionResult> Index()
    {
        var tenants = await _tenantService.ListAsync();
        return View(tenants);
    }

    [HttpGet("{id:guid}")]
    [AdminPermission(AdminPermissions.TenantsIndex)]
    public async Task<IActionResult> Details(Guid id)
    {
        var tenant = await _tenantService.GetAsync(id);
        if (tenant is null)
        {
            return NotFound();
        }

        var newsletters = await _dbContext.NewsletterLists
            .Where(list => list.TenantId == id)
            .Select(list => new TenantNewsletterSummaryViewModel
            {
                Id = list.Id,
                Name = list.Name,
                Status = list.Status,
                ActiveSubscriptionCount = list.Subscriptions.Count(subscription => subscription.Status == SubscriptionStatus.Active)
            })
            .OrderBy(list => list.Name)
            .ToListAsync();
        var managers = await _tenantManagerService.ListManagersAsync(id);

        return View(new TenantDetailViewModel
        {
            Tenant = tenant,
            NewsletterLists = newsletters,
            Managers = managers.Select(manager => new TenantManagerItemViewModel
            {
                UserId = manager.UserId,
                Email = manager.Email,
                DisplayName = manager.DisplayName,
                CreatedAt = manager.CreatedAt
            }).ToList(),
            CanManageManagers = User.IsInRole(AdminPermissions.AdminRole) || User.IsInRole(AdminPermissions.SuperuserRole)
        });
    }

    [HttpPost("{id:guid}/managers")]
    [AdminPermission(AdminPermissions.TenantsEdit)]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> AssignManager(Guid id, string email)
    {
        if (string.IsNullOrWhiteSpace(email))
        {
            TempData["Error"] = "Manager email is required.";
            return RedirectToAction("Details", new { id });
        }

        var tenant = await _tenantService.GetAsync(id);
        if (tenant is null)
        {
            return NotFound();
        }

        var user = await _userManager.FindByEmailAsync(email.Trim());
        if (user is null)
        {
            TempData["Error"] = "No member was found with that email.";
            return RedirectToAction("Details", new { id });
        }

        var actorId = GetCurrentUserId();
        var assigned = await _tenantManagerService.AssignAsync(id, user.Id, actorId);
        TempData[assigned ? "Result" : "Error"] = assigned
            ? "Tenant manager assigned."
            : "Unable to assign tenant manager.";
        return RedirectToAction("Details", new { id });
    }

    [HttpPost("{id:guid}/managers/{userId:guid}/delete")]
    [AdminPermission(AdminPermissions.TenantsEdit)]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RemoveManager(Guid id, Guid userId)
    {
        var removed = await _tenantManagerService.RemoveAsync(id, userId, GetCurrentUserId());
        TempData[removed ? "Result" : "Error"] = removed
            ? "Tenant manager removed."
            : "Unable to remove tenant manager.";
        return RedirectToAction("Details", new { id });
    }

    [HttpGet("create")]
    [AdminPermission(AdminPermissions.TenantsCreate)]
    public IActionResult Create()
    {
        return View(new TenantFormViewModel());
    }

    [HttpPost("create")]
    [AdminPermission(AdminPermissions.TenantsCreate)]
    public async Task<IActionResult> Create(TenantFormViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        if (!TryParseWebhookClientId(model.SendEngineWebhookClientId, out var webhookClientId))
        {
            ModelState.AddModelError(nameof(model.SendEngineWebhookClientId), "Webhook client id must be a valid UUID.");
            return View(model);
        }

        var domains = model.Domains.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
        await _tenantService.CreateAsync(model.Name, domains, model.Status, webhookClientId);
        return RedirectToAction("Index");
    }

    [HttpGet("edit/{id:guid}")]
    [AdminPermission(AdminPermissions.TenantsEdit)]
    public async Task<IActionResult> Edit(Guid id)
    {
        var tenant = await _tenantService.GetAsync(id);
        if (tenant is null)
        {
            return NotFound();
        }

        return View(new TenantFormViewModel
        {
            Id = tenant.Id,
            Name = tenant.Name,
            Domains = string.Join(",", tenant.Domains),
            Status = tenant.Status,
            SendEngineWebhookClientId = tenant.SendEngineWebhookClientId?.ToString()
        });
    }

    [HttpPost("edit/{id:guid}")]
    [AdminPermission(AdminPermissions.TenantsEdit)]
    public async Task<IActionResult> Edit(Guid id, TenantFormViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        if (!TryParseWebhookClientId(model.SendEngineWebhookClientId, out var webhookClientId))
        {
            ModelState.AddModelError(nameof(model.SendEngineWebhookClientId), "Webhook client id must be a valid UUID.");
            return View(model);
        }

        var domains = model.Domains.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
        var updated = await _tenantService.UpdateAsync(id, model.Name, domains, model.Status, webhookClientId);
        if (updated is null)
        {
            return NotFound();
        }

        return RedirectToAction("Index");
    }

    [HttpPost("delete/{id:guid}")]
    [AdminPermission(AdminPermissions.TenantsDelete)]
    public async Task<IActionResult> Delete(Guid id)
    {
        await _tenantService.DeleteAsync(id);
        return RedirectToAction("Index");
    }

    private static bool TryParseWebhookClientId(string? value, out Guid? webhookClientId)
    {
        webhookClientId = null;
        if (string.IsNullOrWhiteSpace(value))
        {
            return true;
        }

        if (!Guid.TryParse(value.Trim(), out var parsed))
        {
            return false;
        }

        webhookClientId = parsed;
        return true;
    }

    private Guid? GetCurrentUserId()
    {
        var userId = _userManager.GetUserId(User);
        if (!Guid.TryParse(userId, out var parsed))
        {
            return null;
        }

        return parsed;
    }
}
