package com.kt.event.user.service.impl;

import com.kt.event.common.exception.BusinessException;
import com.kt.event.common.security.JwtTokenProvider;
import com.kt.event.user.dto.request.ChangePasswordRequest;
import com.kt.event.user.dto.request.RegisterRequest;
import com.kt.event.user.dto.request.UpdateProfileRequest;
import com.kt.event.user.dto.response.ProfileResponse;
import com.kt.event.user.dto.response.RegisterResponse;
import com.kt.event.user.entity.Store;
import com.kt.event.user.entity.User;
import com.kt.event.user.exception.UserErrorCode;
import com.kt.event.user.repository.StoreRepository;
import com.kt.event.user.repository.UserRepository;
import com.kt.event.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * User Service 구현체
 *
 * 사용자 관리 비즈니스 로직 구현
 *
 * @author Backend Developer
 * @since 1.0
 */
@Slf4j
@Service
@Transactional(readOnly = true)
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final StoreRepository storeRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenProvider jwtTokenProvider;

    @Autowired(required = false)
    private RedisTemplate<String, Object> redisTemplate;

    public UserServiceImpl(UserRepository userRepository,
                          StoreRepository storeRepository,
                          PasswordEncoder passwordEncoder,
                          JwtTokenProvider jwtTokenProvider) {
        this.userRepository = userRepository;
        this.storeRepository = storeRepository;
        this.passwordEncoder = passwordEncoder;
        this.jwtTokenProvider = jwtTokenProvider;
    }

    /**
     * 회원가입
     *
     * UFR-USER-010: 회원가입
     */
    @Override
    @Transactional
    public RegisterResponse register(RegisterRequest request) {
        // 1. 이메일 중복 확인
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new BusinessException(UserErrorCode.USER_DUPLICATE_EMAIL.getErrorCode());
        }

        // 2. 전화번호 중복 확인
        if (userRepository.existsByPhoneNumber(request.getPhoneNumber())) {
            throw new BusinessException(UserErrorCode.USER_DUPLICATE_PHONE.getErrorCode());
        }

        // 3. 비밀번호 해싱
        String passwordHash = passwordEncoder.encode(request.getPassword());

        // 4. User 엔티티 생성 및 저장
        User user = User.builder()
                .name(request.getName())
                .phoneNumber(request.getPhoneNumber())
                .email(request.getEmail())
                .passwordHash(passwordHash)
                .role(User.UserRole.OWNER)
                .status(User.UserStatus.ACTIVE)
                .build();

        User savedUser = userRepository.save(user);

        // 5. Store 엔티티 생성 및 저장
        Store store = Store.builder()
                .name(request.getStoreName())
                .industry(request.getIndustry())
                .address(request.getAddress())
                .businessHours(request.getBusinessHours())
                .user(savedUser)
                .build();

        Store savedStore = storeRepository.save(store);

        // 6. JWT 토큰 생성
        String token = jwtTokenProvider.createAccessToken(
                savedUser.getId(),
                savedStore.getId(),
                savedUser.getEmail(),
                savedUser.getName(),
                List.of(savedUser.getRole().name())
        );

        // 7. Redis 세션 저장 (TTL 7일)
        saveSession(token, savedUser.getId(), savedUser.getRole().name());

        // 8. 응답 반환
        return RegisterResponse.builder()
                .token(token)
                .userId(savedUser.getId())
                .userName(savedUser.getName())
                .storeId(savedStore.getId())
                .storeName(savedStore.getName())
                .build();
    }

    /**
     * 프로필 조회
     *
     * UFR-USER-030: 프로필 관리
     */
    @Override
    public ProfileResponse getProfile(UUID userId) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException(UserErrorCode.USER_NOT_FOUND.getErrorCode()));

        Store store = storeRepository.findByUserId(userId)
                .orElse(null);

        return ProfileResponse.builder()
                .userId(user.getId())
                .userName(user.getName())
                .phoneNumber(user.getPhoneNumber())
                .email(user.getEmail())
                .role(user.getRole().name())
                .storeId(store != null ? store.getId() : null)
                .storeName(store != null ? store.getName() : null)
                .industry(store != null ? store.getIndustry() : null)
                .address(store != null ? store.getAddress() : null)
                .businessHours(store != null ? store.getBusinessHours() : null)
                .createdAt(user.getCreatedAt())
                .lastLoginAt(user.getLastLoginAt())
                .build();
    }

    /**
     * 프로필 수정
     *
     * UFR-USER-030: 프로필 관리
     */
    @Override
    @Transactional
    public ProfileResponse updateProfile(UUID userId, UpdateProfileRequest request) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException(UserErrorCode.USER_NOT_FOUND.getErrorCode()));

        // User 정보 수정
        user.updateProfile(request.getName(), request.getEmail(), request.getPhoneNumber());

        // Store 정보 수정
        Store store = storeRepository.findByUserId(userId).orElse(null);
        if (store != null) {
            store.updateInfo(
                    request.getStoreName(),
                    request.getIndustry(),
                    request.getAddress(),
                    request.getBusinessHours()
            );
        }

        return getProfile(userId);
    }

    /**
     * 비밀번호 변경
     *
     * UFR-USER-030: 프로필 관리
     */
    @Override
    @Transactional
    public void changePassword(UUID userId, ChangePasswordRequest request) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException(UserErrorCode.USER_NOT_FOUND.getErrorCode()));

        // 현재 비밀번호 검증
        if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPasswordHash())) {
            throw new BusinessException(UserErrorCode.PWD_INVALID_CURRENT.getErrorCode());
        }

        // 새 비밀번호가 현재 비밀번호와 동일한지 확인
        if (passwordEncoder.matches(request.getNewPassword(), user.getPasswordHash())) {
            throw new BusinessException(UserErrorCode.PWD_SAME_AS_CURRENT.getErrorCode());
        }

        // 새 비밀번호 해싱 및 저장
        String newPasswordHash = passwordEncoder.encode(request.getNewPassword());
        user.changePassword(newPasswordHash);
    }

    /**
     * 최종 로그인 시각 업데이트 (비동기)
     *
     * UFR-USER-020: 로그인
     */
    @Override
    @Async
    @Transactional
    public void updateLastLoginAt(UUID userId) {
        userRepository.updateLastLoginAt(userId, LocalDateTime.now());
    }

    /**
     * Redis 세션 저장 (Redis가 활성화된 경우에만)
     *
     * @param token JWT 토큰
     * @param userId 사용자 ID
     * @param role 역할
     */
    private void saveSession(String token, UUID userId, String role) {
        if (redisTemplate != null) {
            String key = "user:session:" + token;
            String value = userId + ":" + role;
            redisTemplate.opsForValue().set(key, value, 7, TimeUnit.DAYS);
            log.debug("Redis session saved: userId={}", userId);
        } else {
            log.warn("Redis is disabled. Session not saved to Redis.");
        }
    }
}
