package gc.mda.kcg.auth;

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import gc.mda.kcg.auth.dto.AuthResponse;
import gc.mda.kcg.auth.entity.LoginHistory;
import gc.mda.kcg.auth.entity.User;
import gc.mda.kcg.auth.repository.LoginHistoryRepository;
import gc.mda.kcg.auth.repository.UserRepository;
import gc.mda.kcg.config.AppProperties;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;

@Slf4j
@Service
@RequiredArgsConstructor
public class AuthService {

    private final AppProperties appProperties;
    private final JwtProvider jwtProvider;
    private final UserRepository userRepository;
    private final LoginHistoryRepository loginHistoryRepository;

    /**
     * Google id_token 검증 후 사용자 upsert 및 로그인 이력 기록
     */
    @Transactional
    public AuthResponse authenticateWithGoogle(String credential) {
        GoogleIdToken idToken = verifyGoogleToken(credential);
        GoogleIdToken.Payload payload = idToken.getPayload();

        String email = payload.getEmail();
        String name = (String) payload.get("name");
        String picture = (String) payload.get("picture");

        validateEmailDomain(email);

        User user = upsertUser(email, name, picture);
        recordLoginHistory(user);

        return AuthResponse.builder()
                .email(user.getEmail())
                .name(user.getName())
                .picture(user.getPicture())
                .build();
    }

    /**
     * JWT 토큰에서 사용자 정보 조회
     */
    @Transactional(readOnly = true)
    public AuthResponse getUserFromToken(String token) {
        Claims claims = jwtProvider.validateToken(token);
        String email = claims.getSubject();

        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new IllegalArgumentException("사용자를 찾을 수 없습니다: " + email));

        return AuthResponse.builder()
                .email(user.getEmail())
                .name(user.getName())
                .picture(user.getPicture())
                .build();
    }

    private GoogleIdToken verifyGoogleToken(String credential) {
        try {
            GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
                    new NetHttpTransport(), GsonFactory.getDefaultInstance())
                    .setAudience(Collections.singletonList(appProperties.getGoogle().getClientId()))
                    .build();

            GoogleIdToken idToken = verifier.verify(credential);
            if (idToken == null) {
                throw new IllegalArgumentException("유효하지 않은 Google 토큰입니다.");
            }
            return idToken;
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("Google 토큰 검증 실패", e);
            throw new IllegalArgumentException("Google 토큰 검증에 실패했습니다.", e);
        }
    }

    private void validateEmailDomain(String email) {
        String allowedDomain = appProperties.getAuth().getAllowedDomain();
        if (allowedDomain != null && !allowedDomain.isBlank()) {
            String domain = email.substring(email.indexOf('@') + 1);
            if (!domain.equals(allowedDomain)) {
                throw new IllegalArgumentException("허용되지 않은 이메일 도메인입니다: " + domain);
            }
        }
    }

    private User upsertUser(String email, String name, String picture) {
        return userRepository.findByEmail(email)
                .map(existing -> {
                    existing.setName(name);
                    existing.setPicture(picture);
                    existing.setLastLoginAt(LocalDateTime.now());
                    return userRepository.save(existing);
                })
                .orElseGet(() -> {
                    User newUser = User.builder()
                            .email(email)
                            .name(name)
                            .picture(picture)
                            .lastLoginAt(LocalDateTime.now())
                            .build();
                    return userRepository.save(newUser);
                });
    }

    private void recordLoginHistory(User user) {
        LoginHistory history = LoginHistory.builder()
                .user(user)
                .loginAt(LocalDateTime.now())
                .build();
        loginHistoryRepository.save(history);
    }
}
