package de.szut.casino.security.oauth2.google;

import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.jwt.JwtUtils;
import de.szut.casino.user.AuthProvider;
import de.szut.casino.user.UserEntity;
import de.szut.casino.user.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Service
public class GoogleService {
    private static final Logger logger = LoggerFactory.getLogger(GoogleService.class);

    @Value("${spring.security.oauth2.client.registration.google.client-id}")
    private String clientId;

    @Value("${spring.security.oauth2.client.registration.google.client-secret}")
    private String clientSecret;

    @Value("${spring.security.oauth2.client.registration.google.redirect-uri}")
    private String redirectUri;

    @Value("${spring.security.oauth2.client.provider.google.token-uri}")
    private String tokenUri;

    @Value("${spring.security.oauth2.client.provider.google.user-info-uri}")
    private String userInfoUri;

    private final AuthenticationManager authenticationManager;
    private final UserRepository userRepository;
    private final JwtUtils jwtUtils;
    private final PasswordEncoder oauth2PasswordEncoder;

    public GoogleService(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtils jwtUtils, PasswordEncoder oauth2PasswordEncoder) {
        this.authenticationManager = authenticationManager;
        this.userRepository = userRepository;
        this.jwtUtils = jwtUtils;
        this.oauth2PasswordEncoder = oauth2PasswordEncoder;
    }

    public AuthResponseDto processGoogleCode(String code) {
        try {
            RestTemplate restTemplate = new RestTemplate();

            HttpHeaders tokenHeaders = new HttpHeaders();
            tokenHeaders.set("Content-Type", "application/x-www-form-urlencoded");
            
            MultiValueMap<String, String> tokenRequestBody = new LinkedMultiValueMap<>();
            tokenRequestBody.add("client_id", clientId);
            tokenRequestBody.add("client_secret", clientSecret);
            tokenRequestBody.add("code", code);
            tokenRequestBody.add("redirect_uri", redirectUri);
            tokenRequestBody.add("grant_type", "authorization_code");

            HttpEntity<MultiValueMap<String, String>> tokenRequestEntity = new HttpEntity<>(tokenRequestBody, tokenHeaders);

            ResponseEntity<Map> tokenResponse = restTemplate.exchange(
                    tokenUri,
                    HttpMethod.POST,
                    tokenRequestEntity,
                    Map.class
            );

            Map<String, Object> tokenResponseBody = tokenResponse.getBody();

            if (tokenResponseBody == null || tokenResponseBody.containsKey("error")) {
                String error = tokenResponseBody != null ? (String) tokenResponseBody.get("error") : "Unknown error";
                throw new RuntimeException("Google OAuth error: " + error);
            }

            String accessToken = (String) tokenResponseBody.get("access_token");
            if (accessToken == null || accessToken.isEmpty()) {
                throw new RuntimeException("Failed to receive access token from Google");
            }

            HttpHeaders userInfoHeaders = new HttpHeaders();
            userInfoHeaders.set("Authorization", "Bearer " + accessToken);

            HttpEntity<String> userInfoRequestEntity = new HttpEntity<>(null, userInfoHeaders);

            ResponseEntity<Map> userResponse = restTemplate.exchange(
                    userInfoUri,
                    HttpMethod.GET,
                    userInfoRequestEntity,
                    Map.class
            );

            Map<String, Object> userAttributes = userResponse.getBody();
            if (userAttributes == null) {
                throw new RuntimeException("Failed to fetch user data from Google");
            }

            String googleId = (String) userAttributes.get("sub");
            String email = (String) userAttributes.get("email");
            String name = (String) userAttributes.get("name");
            Boolean emailVerified = (Boolean) userAttributes.getOrDefault("email_verified", false);

            if (email == null) {
                throw new RuntimeException("Google account does not have an email");
            }

            String username = name != null ? name.replaceAll("\\s+", "") : email.split("@")[0];

            Optional<UserEntity> userOptional = userRepository.findByProviderId(googleId);
            UserEntity user;

            if (userOptional.isPresent()) {
                user = userOptional.get();
            } else {
                userOptional = userRepository.findByEmail(email);

                if (userOptional.isPresent()) {
                    user = userOptional.get();
                    user.setProvider(AuthProvider.GOOGLE);
                    user.setProviderId(googleId);
                } else {
                    user = new UserEntity();
                    user.setEmail(email);
                    user.setUsername(username);
                    user.setProvider(AuthProvider.GOOGLE);
                    user.setProviderId(googleId);
                    user.setEmailVerified(emailVerified);
                    
                    user.setBalance(new BigDecimal("100.00"));
                }
            }

            String randomPassword = UUID.randomUUID().toString();
            user.setPassword(oauth2PasswordEncoder.encode(randomPassword));

            userRepository.save(user);

            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getEmail(), randomPassword)
            );

            String token = jwtUtils.generateToken(authentication);

            return new AuthResponseDto(token);

        } catch (Exception e) {
            logger.error("Failed to process Google authentication", e);
            throw new RuntimeException("Failed to process Google authentication", e);
        }
    }
}
