using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.EntityFrameworkCore;
using bitforum.Helpers;
using bitforum.Models;
using bitforum.Models.Account;
using bitforum.Services;
using bitforum.Constants;

namespace bitforum.Controllers.Member
{
    [Authorize]
    [Route("Member")]
    public class ListController : Controller
    {
        private readonly ILogger<ListController> _logger;
        private readonly IFileUploadService _fileUploadService;
        private readonly DefaultDbContext _db;
        private readonly string _IndexViewPath = "~/Views/Member/List/Index.cshtml";
        private readonly string _WriteViewPath = "~/Views/Member/List/Write.cshtml";
        private readonly string _EditViewPath = "~/Views/Member/List/Edit.cshtml";
        private string? _queryString = null;

        public ListController(ILogger<ListController> logger, IFileUploadService fileUploadService, DefaultDbContext db)
        {
            _logger = logger;
            _fileUploadService = fileUploadService;
            _db = db;
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            ViewBag.QueryString = _queryString = string.Join("&", QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).Select(r => $"{r.Key}={r.Value.FirstOrDefault()}"));

            base.OnActionExecuting(context);
        }

        [HttpGet("List")]
        public async Task<IActionResult> Index(
            [FromQuery] 
            int page = 1, int perPage = 10, byte? search = null, string? keyword = null, string startAt = null!, string endAt = null!, sbyte tab = 0, sbyte isEmailVerified = 0, sbyte isAuthCertified = 0, Gender? gender = null
        ) {
            var query = _db.Member.AsQueryable();

            // 검색 조건 적용
            if (!string.IsNullOrEmpty(keyword) && search.HasValue)
            {
                switch (search)
                {
                    case 1: // ID 검색
                        if (int.TryParse(keyword, out int memberID))
                        {
                            query = query.Where(m => m.ID == memberID);
                        }
                        else
                        {
                            query = query.Where(m => m.ID == 0);
                        }
                        break;
                    case 2: // 이메일 검색
                        query = query.Where(m => m.Email.Contains(keyword));
                        break;
                    case 3: // 별명 검색
                        query = query.Where(m => m.Name != null && m.Name.Contains(keyword));
                        break;
                    case 4: // 이름 검색
                        query = query.Where(m => m.FullName != null && m.FullName.Contains(keyword));
                        break;
                    case 5: // 연락처 검색
                        query = query.Where(m => m.Phone != null && m.Phone.Contains(keyword));
                        break;
                }
            }

            if (tab > 0)
            {
                switch (tab)
                {
                    case 1: // 차단
                        query = query.Where(m => m.IsDenied);
                        break;
                    case 2: // 탈퇴
                        query = query.Where(m => m.IsWithdraw);
                        break;
                    case 3: // 관리자
                        query = query.Where(m => m.IsAdmin);
                        break;
                }
            }

            if (gender is not null) // 성별
            {
                query = query.Where(m => m.Gender == gender);
            }

            if (isEmailVerified > 0) // 이메일 인증 여부
            {
                query = query.Where(m => m.IsEmailVerified == true);
            }

            if (isAuthCertified > 0) // 본인 인증 여부
            {
                query = query.Where(m => m.IsAuthCertified == true);
            }

            // 가입 일시
            var today = DateTime.UtcNow.Date;
            if (startAt != null && DateTime.TryParse(startAt, out var st))
            {
                query = query.Where(log => log.CreatedAt >= st);
            }
            if (endAt != null && DateTime.TryParse(endAt, out var et))
            {
                query = query.Where(log => log.CreatedAt <= et);
            }

            query = query.OrderByDescending(c => c.CreatedAt);

            var members = await query
                .Skip((page - 1) * perPage)
                .Take(perPage)
                .Select(c => new
                {
                    GradeName = _db.MemberGrade.Where(m => m.ID == c.GradeID).Select(m => m.KorName).FirstOrDefault(), // 회원등급 이름
                    c.ID,
                    c.SID,
                    c.Email,
                    c.Name,
                    c.FullName,
                    c.Icon,
                    c.Coin,
                    c.Exp,
                    c.Phone,
                    c.Gender,
                    c.Birthday,
                    c.Following,
                    c.Followed,
                    c.IsEmailVerified,
                    c.IsAuthCertified,
                    c.IsDenied,
                    c.IsAdmin,
                    c.IsWithdraw,
                    c.LastLoginIp,
                    c.LastLoginAt,
                    c.CreatedAt,
                    c.UpdatedAt
                })
                .ToListAsync();


            var data = new List<object>();

            if (members.Count > 0)
            {
                foreach (var row in members)
                {
                    data.Add(new
                    {
                        row.GradeName,
                        row.ID,
                        row.Email,
                        row.Name,
                        row.FullName,
                        row.Icon,
                        Phone = row?.Phone ?? "-",
                        Gender = row.Gender.HasValue ? row.Gender.ToString() : "-",
                        Birthday = row.Birthday?.ToString("yyyy.MM.dd"),
                        Coin = row.Coin.ToString("N0"),
                        Exp = row.Exp.ToString("N0"),
                        Following = row.Following.ToString("N0"),
                        Followed = row.Followed.ToString("N0"),
                        row.IsEmailVerified,
                        row.IsAuthCertified,
                        row.IsDenied,
                        row.IsAdmin,
                        row.IsWithdraw,
                        row.LastLoginIp,
                        LastLoginAt = row.LastLoginAt?.ToString("yyyy.MM.dd"),
                        CreatedAt = row.CreatedAt.ToString("yyyy.MM.dd"),
                        UpdatedAt = row.UpdatedAt?.ToString("yyyy.MM.dd"),
                        ApproveURL = $"/Member/List/{row.ID}/Approve?{_queryString}",
                        EditURL = $"/Member/List/{row.ID}/Edit?{_queryString}",
                        DeleteURL = $"/Member/List/{row.ID}/Delete?{_queryString}"
                    });
                }
            }

            var parameter = new
            {
                Page = page,
                PerPage = perPage,
                Search = search,
                Keyword = keyword,
                StartAt = startAt,
                EndAt = endAt,
                Tab = tab,
                IsEmailVerified = isEmailVerified,
                IsAuthCertified = isAuthCertified,
                Gender = gender,
            };

            ViewBag.Data = data;
            ViewBag.Total = await query.CountAsync();
            ViewBag.Parameter = parameter;
            ViewBag.Pagination = new Pagination(ViewBag.Total, page, perPage, parameter);

            return View(_IndexViewPath);
        }

        [HttpGet("List/Write")]
        public IActionResult Write()
        {
            ViewBag.MemberGrades = new SelectList(
                _db.MemberGrade.Where(c => c.IsActive).OrderByDescending(c => c.Order),
                "ID",
                "KorName",
                null
            );

            return View(_WriteViewPath);
        }

        [HttpPost("List/Create")]
        public async Task<IActionResult> Create([FromForm] Models.Account.Member request, IFormFile? photo, IFormFile? icon)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    throw new Exception("유효성 검사에 실패하였습니다.");
                }

                if (request.FirstName != null || request.LastName != null)
                {
                    request.FullName = $"{request.FirstName}{request.LastName}";
                }

                // 이메일 중복 확인
                if (await _db.Member.AnyAsync(c => c.Email == request.Email))
                {
                    throw new ArgumentException("이미 등록된 이메일 주소입니다.");
                }

                // 별명 중복 확인
                if (await _db.Member.AnyAsync(c => c.Name == request.Name))
                {
                    throw new ArgumentException("이미 등록된 별명입니다.");
                }
          
                request.Password = BCrypt.Net.BCrypt.HashPassword(request.Password);
                request.PasswordUpdatedAt = DateTime.UtcNow;
                request.SignupIP = HttpContext.GetClientIP();
                request.CreatedAt = DateTime.UtcNow;

                await _db.Member.AddAsync(request);

                int affectedRows = await _db.SaveChangesAsync();
                if (affectedRows <= 0)
                {
                    throw new Exception("회원 등록 중 오류가 발생했습니다.");
                }

                request.Photo = await _fileUploadService.UploadImageAsync(photo, UploadFolder.Member, request.ID);
                request.Icon = await _fileUploadService.UploadImageAsync(icon, UploadFolder.Member, request.ID);

                request.EmailVerifiedAt = (request.IsEmailVerified ? DateTime.UtcNow : null);
                request.AuthCertifiedAt = (request.IsAuthCertified ? DateTime.UtcNow : null);
                request.DeletedAt = (request.IsWithdraw ? DateTime.UtcNow : null);

                // 회원의 약관 및 알림 동의 정보 생성
                await _db.MemberApprove.AddAsync(new MemberApprove
                {
                    MemberID = request.ID,
                    IsReceiveSMS = false,
                    IsReceiveEmail = false,
                    IsReceiveNote = false,
                    IsDisclosureInvest = false
                });

                await _db.SaveChangesAsync();

                string message = "회원이 등록되었습니다.";
                TempData["SuccessMessage"] = message;
                _logger.LogInformation(message);
                return Redirect("/Member/List");
            }
            catch (ArgumentException e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
                return Write();
            }
            catch (Exception e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
                return Write();
            }
        }

        [HttpGet("List/{id}/Edit")]
        public async Task<IActionResult> Edit(int id)
        {
            try
            {
                if (id <= 0)
                {
                    throw new Exception("유효하지 않은 접근입니다.");
                }

                var member = await _db.Member.Where(m => m.ID == id).FirstOrDefaultAsync();
                if (member is null)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                ViewBag.MemberGrades = new SelectList(
                    _db.MemberGrade.Where(c => c.IsActive).OrderByDescending(c => c.Order),
                    "ID",
                    "KorName",
                    member.GradeID
                );

                return View(_EditViewPath, member);
            }
            catch (Exception e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
                return Redirect("/Member/List");
            }
        }

        [HttpPost("List/Update")]
        public async Task<IActionResult> Update([FromForm] Models.Account.Member request, IFormFile? photo, IFormFile? icon, [FromForm] bool IsPhotoRemove = false, bool IsIconRemove = false)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    throw new Exception("유효성 검사에 실패하였습니다.");
                }

                var member = await _db.Member.FirstOrDefaultAsync(c => c.ID == request.ID);
                if (member is null)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                if (request.FirstName != null || request.LastName != null)
                {
                    member.FullName = $"{request.FirstName}{request.LastName}";
                }

                // 이메일 중복 확인
                if (await _db.Member.AnyAsync(c => c.Email == request.Email && c.ID != member.ID))
                {
                    throw new ArgumentException("이미 등록된 이메일 주소입니다.");
                }

                // 별명 중복 확인
                if (await _db.Member.AnyAsync(c => c.Name == request.Name && c.ID != member.ID))
                {
                    throw new ArgumentException("이미 등록된 별명입니다.");
                }

                // 비밀번호 변경
                if (!string.IsNullOrEmpty(request.Password))
                {
                    member.Password = BCrypt.Net.BCrypt.HashPassword(request.Password);
                    member.PasswordUpdatedAt = DateTime.UtcNow;
                }

                // 사진 저장
                if (IsPhotoRemove)
                {
                    _fileUploadService.RemoveFile(request.Photo);
                    member.Photo = null;
                }
                else if (photo is not null)
                {
                    member.Photo = await _fileUploadService.UploadImageAsync(photo, UploadFolder.Grade, member.ID);
                }

                // 아이콘 저장
                if (IsIconRemove)
                {
                    _fileUploadService.RemoveFile(request.Photo);
                    member.Icon = null;
                }
                else if (icon is not null)
                {
                    member.Icon = await _fileUploadService.UploadImageAsync(icon, UploadFolder.Grade, member.ID);
                }

                member.GradeID = request.GradeID;
                member.Email = request.Email;
                member.LastEmailChangedAt = (member.Email != request.Email ? DateTime.UtcNow : null);
                member.Name = request.Name;
                member.LastNameChangedAt = (member.Name != request.Name ? DateTime.UtcNow : null);
                member.FirstName = request.FirstName;
                member.LastName = request.LastName;
                member.Intro = request.Intro;
                member.Summary = request.Summary;
                member.Phone = request.Phone;
                member.Birthday = request.Birthday;
                member.Gender = request.Gender;
                member.EmailVerifiedAt = (request.IsEmailVerified && !member.IsEmailVerified && member.IsEmailVerified != request.IsEmailVerified ? DateTime.UtcNow : (request.IsEmailVerified ? member.EmailVerifiedAt : null));
                member.IsEmailVerified = request.IsEmailVerified;
                member.AuthCertifiedAt = (request.IsAuthCertified && !member.IsAuthCertified && member.IsAuthCertified != request.IsAuthCertified ? DateTime.UtcNow : (request.IsAuthCertified ? member.AuthCertifiedAt : null));
                member.IsAuthCertified = request.IsAuthCertified;
                member.IsDenied = request.IsDenied;
                member.IsAdmin = request.IsAdmin;
                member.DeletedAt = (request.IsWithdraw && !member.IsWithdraw && member.IsWithdraw != request.IsWithdraw  ? DateTime.UtcNow : (request.IsWithdraw ? member.DeletedAt : null));
                member.IsWithdraw = request.IsWithdraw;
                member.UpdatedAt = DateTime.UtcNow;

                _db.Member.Update(member);

                int affectedRows = await _db.SaveChangesAsync();
                if (affectedRows <= 0)
                {
                    throw new Exception("회원 수정 중 오류가 발생했습니다.");
                }

                string message = "회원 정보가 수정되었습니다.";
                TempData["SuccessMessage"] = message;
                _logger.LogInformation(message);
                return Redirect($"/Member/List/{request.ID}/Edit?{_queryString}");
            }
            catch (Exception e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
                return await Edit(request.ID);
            }
        }

        [HttpGet("List/{id}/Delete")]
        public async Task<IActionResult> Delete(int id)
        {
            try
            {
                if (id <= 0)
                {
                    throw new Exception("유효하지 않은 접근입니다.");
                }

                var isMember = await _db.Member.AnyAsync(c => c.ID == id);
                if (!isMember)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                var member = await _db.Member.FindAsync(id);

                _db.Member.Remove(member);

                int affectedRows = await _db.SaveChangesAsync();
                if (affectedRows <= 0)
                {
                    throw new Exception("회원 삭제 중 오류가 발생했습니다.");
                }

                _fileUploadService.RemoveFile(member.Photo);
                _fileUploadService.RemoveFile(member.Icon);

                string message = "회원이 정상적으로 삭제되었습니다.";
                TempData["SuccessMessage"] = message;
                _logger.LogInformation(message);
            }
            catch (Exception e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
            }

            return Redirect($"/Member/List?{_queryString}");
        }

        [HttpPost("List/Delete")]
        public async Task<IActionResult> Delete([FromForm] int[] ids)
        {
            try
            {
                if (ids == null || ids.Length <= 0)
                {
                    throw new Exception("유효하지 않은 접근입니다.");
                }

                foreach (var id in ids)
                {
                    var member = await _db.Member.FindAsync(id);
                    if (member == null)
                    {
                        throw new Exception($"{id}번 회원을 찾을 수 없습니다.");
                    }

                    _db.Member.Remove(member);

                    int affectedRows = await _db.SaveChangesAsync();
                    if (affectedRows <= 0)
                    {
                        throw new Exception($"{id}번 회원 삭제 중 오류가 발생했습니다.");
                    }

                    _fileUploadService.RemoveFile(member.Photo);
                    _fileUploadService.RemoveFile(member.Icon);
                }

                string message = $"{ids.Length}건의 회원이 삭제되었습니다.";
                TempData["SuccessMessage"] = message;
                _logger.LogInformation(message);
            }
            catch (Exception e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
            }

            return Redirect($"/Member/List?{_queryString}");
        }

        [HttpGet("List/{id}/Approve")]
        public async Task<IActionResult> Approve(int id)
        {
            try
            {
                if (id <= 0)
                {
                    throw new Exception("유효하지 않은 접근입니다.");
                }

                var memberApprove = await _db.MemberApprove.FindAsync(id);
                if (memberApprove is null)
                {
                    throw new Exception("회원 알림 및 동의 정보를 찾을 수 없습니다.");
                }

                return View("~/Views/Member/List/Approve.cshtml", memberApprove);
            }
            catch (Exception e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
                return Redirect("/Member/List");
            }
        }

        [HttpPost("List/Approve")]
        public async Task<IActionResult> Approve([FromForm] MemberApprove request)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    throw new Exception("유효성 검사에 실패하였습니다.");
                }

                if (!await _db.MemberApprove.AnyAsync(c => c.MemberID == request.MemberID))
                {
                    throw new ArgumentException("회원 알림 및 동의 정보를 찾을 수 없습니다.");
                }

                _db.MemberApprove.Update(request);

                int affectedRows = await _db.SaveChangesAsync();
                if (affectedRows <= 0)
                {
                    throw new Exception("수정 중 오류가 발생했습니다.");
                }

                string message = "회원 알림 및 동의 정보가 수정되었습니다.";
                TempData["SuccessMessage"] = message;
                _logger.LogInformation(message);
                return Redirect("/Member/List");
            }
            catch (Exception e)
            {
                TempData["ErrorMessages"] = e.Message;
                _logger.LogError(e, e.Message);
                return await Approve(request.MemberID);
            }
        }
    }
}
