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

namespace MemberCenter.Web.Areas.Admin.Controllers;

[Area("Admin")]
[Authorize(Policy = "Admin")]
[AdminPermission(AdminPermissions.Accounts)]
[Route("admin/accounts")]
public class AccountsController : Controller
{
    private readonly IAccountGovernanceService _accountGovernanceService;
    private readonly IEmailBlacklistService _emailBlacklistService;
    private readonly INewsletterService _newsletterService;
    private readonly IProfileService _profileService;
    private readonly UserManager<ApplicationUser> _userManager;

    public AccountsController(
        IAccountGovernanceService accountGovernanceService,
        IEmailBlacklistService emailBlacklistService,
        INewsletterService newsletterService,
        IProfileService profileService,
        UserManager<ApplicationUser> userManager)
    {
        _accountGovernanceService = accountGovernanceService;
        _emailBlacklistService = emailBlacklistService;
        _newsletterService = newsletterService;
        _profileService = profileService;
        _userManager = userManager;
    }

    [HttpGet("")]
    [AdminPermission(AdminPermissions.AccountsIndex)]
    public async Task<IActionResult> Index(string? search = null, string? status = null, int page = 1, int pageSize = 20)
    {
        var operatorIsSuperuser = User.IsInRole(AdminPermissions.SuperuserRole);
        status = NormalizeStatus(status, operatorIsSuperuser);
        page = Math.Max(1, page);
        pageSize = Math.Clamp(pageSize, 10, 100);

        if (status == "blacklisted")
        {
            var blacklistItems = await _emailBlacklistService.ListAsync(1000);
            if (!string.IsNullOrWhiteSpace(search))
            {
                blacklistItems = blacklistItems
                    .Where(item => item.Email.Contains(search, StringComparison.OrdinalIgnoreCase)
                        || item.Reason.Contains(search, StringComparison.OrdinalIgnoreCase)
                        || item.BlacklistedBy.Contains(search, StringComparison.OrdinalIgnoreCase))
                    .ToList();
            }

            var totalBlacklistItems = blacklistItems.Count;
            blacklistItems = blacklistItems.Skip((page - 1) * pageSize).Take(pageSize).ToList();

            return View(new AccountsIndexViewModel
            {
                Search = search,
                StatusFilter = status,
                Page = page,
                PageSize = pageSize,
                TotalCount = totalBlacklistItems,
                CanManage = User.IsInRole("superuser"),
                OperatorIsSuperuser = operatorIsSuperuser,
                BlacklistItems = blacklistItems
            });
        }

        var items = await _accountGovernanceService.ListUsersAsync(search, 1000);
        items = ApplyStatusFilter(items, status);
        var totalItems = items.Count;
        items = items.Skip((page - 1) * pageSize).Take(pageSize).ToList();

        return View(new AccountsIndexViewModel
        {
            Search = search,
            StatusFilter = status,
            Page = page,
            PageSize = pageSize,
            TotalCount = totalItems,
            CanManage = User.IsInRole("superuser"),
            OperatorIsSuperuser = operatorIsSuperuser,
            Items = items
        });
    }

    [HttpGet("{id:guid}")]
    [AdminPermission(AdminPermissions.AccountsIndex)]
    public async Task<IActionResult> Details(Guid id)
    {
        var user = await _userManager.FindByIdAsync(id.ToString());
        if (user is null)
        {
            return NotFound();
        }

        var profile = await _profileService.GetProfileAsync(id);
        var subscriptions = (await _newsletterService.ListSubscriptionsForUserAsync(id))
            .Where(subscription => string.Equals(subscription.Status, SubscriptionStatus.Active, StringComparison.OrdinalIgnoreCase))
            .ToList();
        var isAdmin = await _userManager.IsInRoleAsync(user, AdminPermissions.AdminRole);
        var isSuperuser = await _userManager.IsInRoleAsync(user, AdminPermissions.SuperuserRole);
        var operatorIsSuperuser = User.IsInRole(AdminPermissions.SuperuserRole);
        var isBlacklisted = await _emailBlacklistService.IsBlacklistedAsync(user.Email ?? string.Empty);
        var profileImage = await _profileService.GetProfileImageAsync(id);
        var mappedProfile = MapProfile(profile, user.EmailConfirmed);
        var displayName = string.Join(" ", new[] { mappedProfile.LastName, mappedProfile.FirstName }
            .Where(value => !string.IsNullOrWhiteSpace(value)));
        if (string.IsNullOrWhiteSpace(displayName))
        {
            displayName = mappedProfile.NickName ?? mappedProfile.Email;
        }
        if (string.IsNullOrWhiteSpace(displayName))
        {
            displayName = "會員";
        }

        return View(new AccountDetailViewModel
        {
            UserId = id,
            DisplayName = displayName,
            IsDisabled = user.DisabledAt.HasValue,
            IsBlacklisted = isBlacklisted,
            IsAdmin = isAdmin,
            IsSuperuser = isSuperuser,
            HasProfileImage = profileImage is not null,
            ProfileImageVersion = profileImage?.UpdatedAt.ToUnixTimeSeconds() ?? 0,
            CreatedAt = user.CreatedAt,
            LastLoginAt = user.LastLoginAt,
            Profile = mappedProfile,
            Subscriptions = subscriptions,
            CanAssignAdmin = operatorIsSuperuser && !isSuperuser && !user.DisabledAt.HasValue,
            CanDisableAccount = !isAdmin && !isSuperuser,
            CanResetPassword = !isSuperuser && (operatorIsSuperuser || !isAdmin),
            CanContact = !isSuperuser
        });
    }

    [HttpGet("{id:guid}/avatar")]
    [AdminPermission(AdminPermissions.AccountsIndex)]
    public async Task<IActionResult> Avatar(Guid id)
    {
        var user = await _userManager.FindByIdAsync(id.ToString());
        if (user is null)
        {
            return NotFound();
        }

        var image = await _profileService.GetProfileImageAsync(id);
        if (image is null)
        {
            return NotFound();
        }

        Response.Headers.CacheControl = "private, max-age=300";
        return File(image.Content, image.ContentType);
    }

    [HttpPost("{id:guid}/admin")]
    [Authorize(Policy = "Superuser")]
    [AdminPermission(AdminPermissions.AccountsSetAdmin)]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> SetAdmin(Guid id, bool enabled, string? search = null, string? status = null, int page = 1, int pageSize = 20, string? returnTo = null)
    {
        var actorId = await GetCurrentUserIdAsync();
        if (!actorId.HasValue)
        {
            return RedirectToAction("Login", "Account", new { area = string.Empty });
        }

        try
        {
            await _accountGovernanceService.SetAdminAsync(actorId.Value, id, enabled);
            TempData["Result"] = enabled ? "Admin granted." : "Admin removed.";
        }
        catch (InvalidOperationException ex)
        {
            TempData["Error"] = ex.Message;
        }

        return RedirectAfterAccountAction(id, returnTo, search, status, page, pageSize);
    }

    [HttpPost("{id:guid}/disabled")]
    [AdminPermission(AdminPermissions.AccountsSetDisabled)]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> SetDisabled(Guid id, bool disabled, string? search = null, string? status = null, int page = 1, int pageSize = 20, string? returnTo = null)
    {
        var actorId = await GetCurrentUserIdAsync();
        if (!actorId.HasValue)
        {
            return RedirectToAction("Login", "Account", new { area = string.Empty });
        }

        try
        {
            await _accountGovernanceService.SetDisabledAsync(actorId.Value, id, disabled);
            TempData["Result"] = disabled ? "Account disabled." : "Account enabled.";
        }
        catch (InvalidOperationException ex)
        {
            TempData["Error"] = ex.Message;
        }

        return RedirectAfterAccountAction(id, returnTo, search, status, page, pageSize);
    }

    [HttpPost("{id:guid}/password-reset")]
    [AdminPermission(AdminPermissions.AccountsResetPassword)]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ResetPassword(Guid id, string newPassword, string? search = null, string? status = null, int page = 1, int pageSize = 20, string? returnTo = null)
    {
        var actorId = await GetCurrentUserIdAsync();
        if (!actorId.HasValue)
        {
            return RedirectToAction("Login", "Account", new { area = string.Empty });
        }

        try
        {
            await _accountGovernanceService.ResetPasswordAsync(actorId.Value, id, newPassword);
            TempData["Result"] = "Password reset completed and existing sessions were revoked.";
        }
        catch (InvalidOperationException ex)
        {
            TempData["Error"] = ex.Message;
        }

        return RedirectAfterAccountAction(id, returnTo, search, status, page, pageSize);
    }

    private IActionResult RedirectAfterAccountAction(Guid id, string? returnTo, string? search, string? status, int page, int pageSize)
    {
        if (string.Equals(returnTo, "details", StringComparison.OrdinalIgnoreCase))
        {
            return RedirectToAction(nameof(Details), new { id });
        }

        return RedirectToAction(nameof(Index), new { search, status, page, pageSize });
    }

    private async Task<Guid?> GetCurrentUserIdAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        return user?.Id;
    }

    private static IReadOnlyList<MemberCenter.Application.Models.Admin.UserGovernanceSummaryDto> ApplyStatusFilter(
        IReadOnlyList<MemberCenter.Application.Models.Admin.UserGovernanceSummaryDto> items,
        string status)
    {
        var query = items.AsEnumerable();

        query = status switch
        {
            "disabled" => query.Where(x => x.IsDisabled),
            "unverified" => query.Where(x => !x.EmailConfirmed),
            "admins" => query.Where(x => x.IsAdmin),
            _ => query
        };

        return query.ToList();
    }

    private static string NormalizeStatus(string? status, bool operatorIsSuperuser)
    {
        return status switch
        {
            "disabled" => "disabled",
            "unverified" => "unverified",
            "blacklisted" => "blacklisted",
            "admins" when operatorIsSuperuser => "admins",
            _ => "all"
        };
    }

    private static MemberCenter.Web.Models.Profile.ProfileViewModel MapProfile(
        MemberCenter.Application.Models.Profile.UserProfileDto profile,
        bool emailConfirmed) =>
        new()
        {
            Email = profile.Email,
            EmailConfirmed = emailConfirmed,
            LastName = profile.LastName,
            FirstName = profile.FirstName,
            NickName = profile.NickName,
            MobilePhone = profile.MobilePhone,
            LandlinePhone = profile.LandlinePhone,
            DateOfBirth = profile.DateOfBirth,
            Gender = profile.Gender,
            CompanyName = profile.CompanyName,
            Department = profile.Department,
            JobTitle = profile.JobTitle,
            CompanyPhone = profile.CompanyPhone,
            TaxId = profile.TaxId,
            InvoiceTitle = profile.InvoiceTitle,
            Remark = profile.Remark
        };
}
