using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using bitforum.DTOs.Request;
using bitforum.DTOs.Response;
using bitforum.Repository;
using bitforum.Services;
using bitforum.Helpers;
using bitforum.Constants;
using bitforum.Models.Log;
using bitforum.Extensions;

namespace bitforum.Controllers.API
{
    [ApiController]
    [Route("api/[controller]")]
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] // JWT 인증 적용
    public class AccountController: ControllerBase
    {
        private readonly ILogger<AccountController> _logger;
        private readonly DefaultDbContext _db;
        private readonly IMemberRepository _memberRepository;
        private readonly IMailService _mailService;
        private readonly IConfigService _configService;
        private ResultDto _result = new ResultDto();

        public AccountController(ILogger<AccountController> logger, DefaultDbContext db, IMemberRepository memberRepository, IMailService mailService, IConfigService configService)
        {
            _logger = logger;
            _db = db;
            _memberRepository = memberRepository;
            _mailService = mailService;
            _configService = configService;
        }

        // 회원 조회
        [HttpGet("Info/{email}")]
        public async Task<ActionResult<ResultDto>> Info([FromRoute] string email)
        {
            try
            {
                if (string.IsNullOrEmpty(email))
                {
                    throw new Exception("이메일을 입력해주세요.");
                }

                var member = await _memberRepository.FindMemberByEmail(email);
                if (member is null)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                _result.Data = member;
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);
                _result.Ok = false;
                _result.Status = StatusCodes.Status400BadRequest;
                _result.Message = e.Message;
            }

            return _result;
        }

        // 별명 수정
        [HttpPost("change-nickname")]
        public async Task<ActionResult<ResultDto>> ChangeNickname([FromBody] ChangeNicknameDto request)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    _result.Errors = ModelState.GetErrors();
                    throw new Exception("유효성 검사에 실패했습니다.");
                }

                // 중복 여부 확인
                var isExist = await _memberRepository.IsExistNickname(request.Name);
                if (isExist)
                {
                    throw new Exception("이미 사용중인 별명입니다.");
                }

                // 회원 조회
                var member = await _memberRepository.FindMemberByID(request.ID);
                if (member is null)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                // 별명 변경 가능 기간 계산 (현재 날짜 - 설정된 변경 기간)
                var changeNicknameDay = Config.Register.ChangeNicknameDay ?? 0;
                if (changeNicknameDay > 0)
                {
                    // 사용자의 마지막 별명 변경 날짜가 설정된 제한일 이내인지 확인
                    if (member.LastNameChangedAt.HasValue)
                    {
                        int daysSinceLastChange = (int)(DateTime.UtcNow - member.LastNameChangedAt.Value).TotalDays; // 남은 일수 계산
                        if (daysSinceLastChange < changeNicknameDay)
                        {
                            throw new Exception($"별명 변경은 {changeNicknameDay - daysSinceLastChange}일 후에 가능합니다.");
                        }
                    }
                }

                await _db.NameChangeLog.AddAsync(new NameChangeLog
                {
                    MemberID = member.ID,
                    BeforeName = member.Name,
                    AfterName = request.Name,
                    CreatedAt = DateTime.UtcNow
                });

                member.Name = request.Name;
                member.LastNameChangedAt = DateTime.UtcNow;

                await _db.SaveChangesAsync();

                _result.Message = "별명이 변경되었습니다.";
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);
                _result.Ok = false;
                _result.Status = StatusCodes.Status400BadRequest;
                _result.Message = e.Message;
            }

            return _result;
        }

        // 이메일 변경
        [HttpPost("change-email")]
        public async Task<ActionResult<ResultDto>> ChangeEmail([FromBody] ChangeEmailDto request)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    _result.Errors = ModelState.GetErrors();
                    throw new Exception("유효성 검사에 실패했습니다.");
                }

                // 중복 여부 확인
                var isExist = await _memberRepository.IsExistEmail(request.Email);
                if (isExist)
                {
                    throw new Exception("이미 사용중인 이메일입니다.");
                }

                // 회원 조회
                var member = await _memberRepository.FindMemberByID(request.ID);
                if (member is null)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                // 이메일 변경 가능 기간 계산 (현재 날짜 - 설정된 변경 기간)
                var changeEmailDay = Config.Register.ChangeEmailDay ?? 0;
                if (changeEmailDay > 0)
                {
                    // 사용자의 마지막 이메일 변경 날짜가 설정된 제한일 이내인지 확인
                    if (member.LastEmailChangedAt.HasValue)
                    {
                        int daysSinceLastChange = (int)(DateTime.UtcNow - member.LastEmailChangedAt.Value).TotalDays; // 남은 일수 계산
                        if (daysSinceLastChange < changeEmailDay)
                        {
                            throw new Exception($"별명 변경은 {changeEmailDay - daysSinceLastChange}일 후에 가능합니다.");
                        }
                    }
                }

                // 이메일 인증 메일 발송
                await _mailService.SendEmailVerifyAsync(request.Email, member, new AdditionalData
                {
                    Email = member.Email
                });

                _result.Message = "이메일 확인을 위해 인증 메일이 전송되었습니다.";
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);
                _result.Ok = false;
                _result.Status = StatusCodes.Status400BadRequest;
                _result.Message = e.Message;
            }

            return _result;
        }

        // 비밀번호 변경
        [HttpPost("change-password")]
        public async Task<ActionResult<ResultDto>> ChangePassword([FromBody] ChangePasswordDto request)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    _result.Errors = ModelState.GetErrors();
                    throw new Exception("유효성 검사에 실패하였습니다.");
                }

                var memberID = User.GetID();

                // 회원 확인
                var isExist = await _memberRepository.IsExistID(memberID);
                if (!isExist)
                {
                    throw new Exception("존재하지 않는 회원입니다.");
                }

                // 회원 조회
                var member = await _memberRepository.FindMemberByID(memberID);
                if (member is null)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                // 현재 비밀번호가 맞는지 확인
                var isValidPassword = BCrypt.Net.BCrypt.Verify(request.CurrentPassword, member.Password);
                if (!isValidPassword)
                {
                    throw new Exception("현재 비밀번호가 일치하지 않습니다.");
                }

                var newPassword = BCrypt.Net.BCrypt.HashPassword(request.NewPassword);
                if (member.Password == newPassword)
                {
                    throw new Exception("기존 비밀번호와 동일합니다.");
                }

                var isValidPolicy = _configService.IsPasswordPolicyValid(request.ConfirmPassword);
                if (!isValidPolicy)
                {
                    string message = "";
                    int minLengthConfig = Config.Register.PasswordMinLength ?? 6;
                    int uppercaseLengthConfig = Config.Register.PasswordUppercaseLength ?? 0;
                    int numbersLengthConfig = Config.Register.PasswordNumbersLength ?? 0;
                    int specialCharsLengthConfig = Config.Register.PasswordSpecialcharsLength ?? 0;

                    // 위에 데이터로 안내 문구 만들어줘
                    if (minLengthConfig > 0)
                    {
                        message += $"최소 {minLengthConfig}자 이상, ";
                    }
                    if (uppercaseLengthConfig > 0)
                    {
                        message += $"대문자 {uppercaseLengthConfig}자 이상, ";
                    }
                    if (numbersLengthConfig > 0)
                    {
                        message += $"숫자 {numbersLengthConfig}자 이상, ";
                    }
                    if (specialCharsLengthConfig > 0)
                    {
                        message += $"특수문자 {specialCharsLengthConfig}자 이상, ";
                    }

                    message = message.TrimEnd(',', ' ');

                    throw new Exception($"입력하신 비밀번호는 사용하실 수 없습니다. 다음 조건을 충족해주세요.\n\n{message}");
                }

                member.Password = newPassword;
                member.PasswordUpdatedAt = DateTime.UtcNow;

                await _db.SaveChangesAsync();
                await _mailService.SendChangedPasswordEmailAsync(member);

                _result.Message = "비밀번호가 변경되었습니다.";
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);
                _result.Ok = false;
                _result.Status = StatusCodes.Status400BadRequest;
                _result.Message = e.Message;
            }

            return _result;
        }

        // 회원탈퇴
        [HttpPost("withdraw")]
        public async Task<ActionResult<ResultDto>> Withdraw()
        {
            try
            {
                var memberID = User.GetID();

                // 회원 확인
                var isExist = await _memberRepository.IsExistID(memberID);
                if (!isExist)
                {
                    throw new Exception("존재하지 않는 회원입니다.");
                }

                // 회원 조회
                var member = await _memberRepository.FindMemberByID(memberID);
                if (member is null)
                {
                    throw new Exception("회원 정보를 찾을 수 없습니다.");
                }

                if (member.IsWithdraw)
                {
                    throw new Exception("이미 탈퇴한 회원입니다.");
                }

                member.IsWithdraw = true;
                member.DeletedAt = DateTime.UtcNow;

                // 탈퇴 완료 메일 발송
                await _mailService.SendWithdrawEmailAsync(member);

                // 로그인 강제 로그아웃(쿠키 삭제)
                Response.Cookies.Delete("accessToken");
                Response.Cookies.Delete("refreshToken");

                _result.Message = "정상적으로 탈퇴되었습니다. 이용해 주셔서 감사합니다.";
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);
                _result.Ok = false;
                _result.Status = StatusCodes.Status400BadRequest;
                _result.Message = e.Message;
            }

            return _result;
        }

        // 로그인 기록
        [HttpGet("login-log")]
        public async Task<ActionResult<ResultDto>> LoginLog([FromQuery] int page = 1, [FromQuery] string type = LoginLogType.Today)
        {
            try
            {
                var memberID = User.GetID();

                // 회원 확인
                var isExist = await _memberRepository.IsExistID(memberID);
                if (!isExist)
                {
                    throw new Exception("존재하지 않는 회원입니다.");
                }
              
                if (!LoginLogType.ValidTypes.Contains(type))
                {
                    throw new ArgumentException("유효하지 않은 타입입니다.");
                }

                DateTime now = DateTime.UtcNow;
                DateTime startDate = now.Date;
                DateTime endOfDate = type switch
                {
                    LoginLogType.Today => now.Date.AddDays(1).AddTicks(-1),
                    LoginLogType.Week => now.AddDays(-7),
                    LoginLogType.Month => now.AddMonths(-1),
                    LoginLogType.HalfYear => now.AddMonths(-6),
                    _ => DateTime.MinValue // 전체 조회
                };

                var query = _db.LoginLog.Where(c => c.MemberID == memberID && c.CreatedAt >= startDate && c.CreatedAt <= endOfDate)
                    .Select(c => new
                    {
                        c.ID,
                        c.MemberID,
                        c.Success,
                        c.IpAddress,
                        c.UserAgent,
                        CreatedAt = c.CreatedAt.ToLocalTime()
                    })
                    .OrderByDescending(c => c.ID)
                    .Skip(page * 10)
                    .Take(10);

                var logs = await query.ToListAsync();
                if (logs.Count == 0)
                {
                    throw new Exception("로그인 기록이 없습니다.");
                }

                _result.Data = logs;
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);
                _result.Ok = false;
                _result.Status = StatusCodes.Status400BadRequest;
                _result.Message = e.Message;
            }

            return _result;
        }
    }
}