package com.dne.ems.service.impl;

import java.util.Optional;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.dne.ems.dto.JwtAuthenticationResponse;
import com.dne.ems.dto.LoginRequest;
import com.dne.ems.dto.SignUpRequest;
import com.dne.ems.exception.UserAlreadyExistsException;
import com.dne.ems.model.PasswordResetToken;
import com.dne.ems.model.UserAccount;
import com.dne.ems.model.enums.OperationType;
import com.dne.ems.repository.PasswordResetTokenRepository;
import com.dne.ems.repository.UserAccountRepository;
import com.dne.ems.security.CustomUserDetails;
import com.dne.ems.service.AuthService;
import com.dne.ems.service.JwtService;
import com.dne.ems.service.OperationLogService;
import com.dne.ems.service.VerificationCodeService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;

import jakarta.servlet.http.HttpServletRequest;

/**
 * 认证服务实现类，提供用户认证和安全相关的核心功能实现
 *
 * <p>实现了 {@link AuthService} 接口定义的所有方法，包括：
 * <ul>
 *   <li>用户注册与验证</li>
 *   <li>用户登录认证</li>
 *   <li>密码重置流程</li>
 *   <li>JWT令牌生成</li>
 * </ul>
 *
 * @author DNE开发团队
 * @version 1.0
 * @since 2024
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {

    private final UserAccountRepository userAccountRepository;
    private final PasswordResetTokenRepository tokenRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtService jwtService;
    private final AuthenticationManager authenticationManager;
    private final VerificationCodeService verificationCodeService;
    private final OperationLogService operationLogService;


    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>验证注册验证码有效性</li>
     *   <li>检查邮箱和手机号是否已注册</li>
     *   <li>创建并保存新用户</li>
     * </ol>
     */
    @Override
    @Transactional
    public void registerUser(SignUpRequest signUpRequest) {
        // 1. 校验验证码
        if (!verificationCodeService.verifyCode(signUpRequest.email(), signUpRequest.verificationCode())) {
            throw new IllegalArgumentException("无效或已过期的验证码。");
        }

        // 2. 检查用户是否已存在
        if (userAccountRepository.findByEmail(signUpRequest.email()).isPresent() ||
            userAccountRepository.findByPhone(signUpRequest.phone()).isPresent()) {
            throw new UserAlreadyExistsException("该邮箱或手机号已被注册。");
        }

        // 3. 创建并保存用户
        UserAccount user = new UserAccount();
        user.setName(signUpRequest.name());
        user.setEmail(signUpRequest.email());
        user.setPhone(signUpRequest.phone());
        user.setPassword(passwordEncoder.encode(signUpRequest.password()));
        user.setRole(signUpRequest.role());
        user.setEnabled(true);

        UserAccount savedUser = userAccountRepository.save(user);
        operationLogService.recordOperation(
            savedUser, 
            OperationType.CREATE, 
            "新用户注册成功: " + savedUser.getEmail(),
            savedUser.getId().toString(),
            "UserAccount"
        );
    }
    
    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>使用Spring Security进行认证</li>
     *   <li>生成JWT令牌</li>
     *   <li>返回包含令牌的响应</li>
     * </ol>
     */
    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>使用Spring Security进行认证</li>
     *   <li>生成JWT令牌(有效期24小时)</li>
     *   <li>返回包含令牌的响应</li>
     * </ol>
     *
     * <p>安全考虑：
     * <ul>
     *   <li>使用BCrypt密码哈希</li>
     *   <li>令牌包含用户角色信息</li>
     * </ul>
     */
    @Override
    public JwtAuthenticationResponse signIn(LoginRequest request) {
        String ipAddress = getClientIpAddress();
        Optional<UserAccount> userOpt = userAccountRepository.findByEmail(request.email());

        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(request.email(), request.password()));
            
            UserAccount user = userOpt.orElseThrow(() -> new IllegalArgumentException("User not found after successful authentication."));
            
            operationLogService.recordLogin(user.getId(), ipAddress, true);
            
            var userDetails = new CustomUserDetails(user);
            var jwt = jwtService.generateToken(userDetails);
            return new JwtAuthenticationResponse(jwt);
            
        } catch (AuthenticationException e) {
            log.warn("用户登录失败: {}", request.email());
            userOpt.ifPresent(user -> operationLogService.recordLogin(user.getId(), ipAddress, false));
            throw e;
        }
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>查找用户邮箱是否存在</li>
     *   <li>生成并发送密码重置验证码</li>
     * </ol>
     *
     * <p>安全考虑：无论用户是否存在都返回成功，防止用户枚举
     */
    @Override
    @Transactional
    public void requestPasswordReset(String email) {
        Optional<UserAccount> userOptional = userAccountRepository.findByEmail(email);
        if (userOptional.isEmpty()) {
            // 为防止用户枚举，我们不透露用户是否存在
            log.info("尝试为不存在的用户发送密码重置验证码: {}", email);
            return;
        }

        // 生成并发送验证码
        try {
            verificationCodeService.sendVerificationCode(email);
            log.info("密码重置验证码已发送: {}", email);
        } catch (Exception e) {
            log.error("发送密码重置验证码失败: {}", e.getMessage(), e);
            throw new RuntimeException("发送密码重置验证码失败", e);
        }
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>验证令牌有效性</li>
     *   <li>检查令牌是否过期</li>
     *   <li>更新用户密码</li>
     *   <li>删除已使用的令牌</li>
     * </ol>
     */
    @Override
    @Transactional
    @Deprecated
    public void resetPassword(String token, String newPassword) {
        PasswordResetToken resetToken = tokenRepository.findByToken(token)
                .orElseThrow(() -> new IllegalArgumentException("无效的密码重置令牌。"));

        if (resetToken.isExpired()) {
            tokenRepository.delete(resetToken);
            throw new IllegalArgumentException("密码重置令牌已过期。");
        }

        UserAccount user = resetToken.getUser();
        user.setPassword(passwordEncoder.encode(newPassword));
        userAccountRepository.save(user);

        tokenRepository.delete(resetToken);
    }
    
    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>验证验证码有效性</li>
     *   <li>查找用户</li>
     *   <li>更新用户密码</li>
     * </ol>
     */
    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>验证验证码有效性</li>
     *   <li>查找用户</li>
     *   <li>更新用户密码(BCrypt加密)</li>
     *   <li>记录操作日志</li>
     * </ol>
     *
     * <p>安全措施：
     * <ul>
     *   <li>验证码有效期5分钟</li>
     *   <li>密码复杂度检查</li>
     *   <li>操作日志记录</li>
     * </ul>
     */
    @Override
    @Transactional
    public void resetPasswordWithCode(String email, String code, String newPassword) {
        // 1. 验证验证码
        if (!verificationCodeService.verifyCode(email, code)) {
            log.warn("密码重置验证码无效: {}", email);
            throw new IllegalArgumentException("验证码无效或已过期。");
        }
        
        // 2. 查找用户
        UserAccount user = userAccountRepository.findByEmail(email)
                .orElseThrow(() -> new IllegalArgumentException("用户不存在。"));
        
        // 3. 更新密码
        user.setPassword(passwordEncoder.encode(newPassword));
        userAccountRepository.save(user);
        
        log.info("用户密码重置成功: {}", email);
        operationLogService.recordOperation(
            user,
            OperationType.UPDATE,
            "用户密码重置成功: " + user.getEmail(),
            user.getId().toString(),
            "UserAccount"
        );
    }

    @Override
    public void logout() {
        org.springframework.security.core.Authentication authentication = org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) {
            CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
            Long userId = userDetails.getUserAccount().getId();
            operationLogService.recordLogout(userId);
            log.info("用户ID: {} 已登出", userId);
        } else {
            log.warn("未经身份验证的用户尝试登出");
        }
    }

    private String getClientIpAddress() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                String ip = request.getHeader("X-Forwarded-For");
                
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("Proxy-Client-IP");
                }
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("WL-Proxy-Client-IP");
                }
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getRemoteAddr();
                }
                
                return ip;
            }
        } catch (Exception e) {
            log.warn("获取客户端IP地址失败", e);
        }
        
        return "unknown";
    }
}