package com.takensoft.common.oauth.web;

import com.takensoft.cms.loginPolicy.service.LoginModeService;
import com.takensoft.cms.mber.service.MberService;
import com.takensoft.cms.mber.vo.MberVO;
import com.takensoft.common.message.MessageCode;
import com.takensoft.common.service.VerificationService;
import com.takensoft.common.util.HttpRequestUtil;
import com.takensoft.common.util.JWTUtil;
import com.takensoft.common.util.ResponseUtil;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;

/**
 * @author takensoft
 * @since 2025.05.22
 * @modification
 *     since    |    author    | description
 *  2025.05.22  |  takensoft   | 최초 등록
 *  2025.05.26  |  takensoft   | OAuth 리다이렉트 기능 추가
 *  2025.05.28  |  takensoft   | 쿠키에서 OAuth 토큰 읽기 추가
 *
 * OAuth2 관련 통합 컨트롤러
 */
@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping(value = "/oauth2")
public class OAuth2Controller {

    private final MberService mberService;
    private final VerificationService verificationService;
    private final ResponseUtil resUtil;
    private final HttpRequestUtil httpRequestUtil;
    private final LoginModeService loginModeService;
    private final JWTUtil jwtUtil;

    @Value("${front.url}")
    private String FRONT_URL;

    // 지원하는 OAuth 제공자 목록
    private static final List<String> SUPPORTED_PROVIDERS = Arrays.asList("kakao", "naver", "google");

    /**
     * OAuth 로그인 리다이렉트 처리
     * 프론트엔드에서 provider 정보를 받아 검증 후 OAuth2 서버로 리다이렉트
     */
    @GetMapping("/login")
    public void redirectToOAuth(@RequestParam String provider, HttpServletRequest request, HttpServletResponse response) throws IOException {

        String clientIP = httpRequestUtil.getIp(request);
        String userAgent = httpRequestUtil.getUserAgent(request);

        try {
            // 1. Provider 유효성 검증
            validateProvider(provider);

            // 2. 보안 검증 (필요시 추가)
            validateSecurity(request);

            // 3. CORS 헤더 설정 (브라우저 보안 문제 해결)
            response.setHeader("Access-Control-Allow-Origin", FRONT_URL);
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
            response.setHeader("Access-Control-Allow-Headers", "*");

            // 4. OAuth2 Authorization Server로 리다이렉트
            String redirectUrl = "/oauth2/authorization/" + provider;

            response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
            response.sendRedirect(redirectUrl);

        } catch (IllegalArgumentException e) {
            handleError(response, "invalid_provider", e.getMessage());
        } catch (SecurityException e) {
            handleError(response, "security_check_failed", e.getMessage());
        } catch (Exception e) {
            handleError(response, "system_error", "OAuth 로그인 중 오류가 발생했습니다.");
        }
    }

    /**
     * OAuth2 로그인 후 사용자 정보 조회
     */
    @PostMapping(value = "/getUserInfo.json")
    public ResponseEntity<?> getUserInfo(HttpServletRequest request) {
        try {
            // 로그인 모드 확인
            String loginMode = loginModeService.getLoginMode();

            // 현재 로그인한 사용자 ID 조회
            String currentUserId = null;
            MberVO mberInfo = null;

            if ("S".equals(loginMode)) {
                // 세션 모드 - 세션에서 직접 조회
                HttpSession session = request.getSession(false);
                if (session != null) {
                    currentUserId = (String) session.getAttribute("mbrId");

                    if (currentUserId != null) {
                        // 세션에 저장된 정보로 응답 생성
                        HashMap<String, Object> result = new HashMap<>();
                        result.put("mbrId", session.getAttribute("mbrId"));
                        result.put("mbrNm", session.getAttribute("mbrNm"));
                        result.put("roles", session.getAttribute("roles"));

                        return resUtil.successRes(result, MessageCode.COMMON_SUCCESS);
                    }
                } else {
                    return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
                }
            } else {
                // JWT 모드 - 토큰에서 조회
                String authHeader = request.getHeader("Authorization");
                System.out.println("Authorization Header: " + authHeader);

                String token = null;

                if (authHeader != null && !authHeader.isEmpty()) {
                    // Authorization 헤더에서 토큰 추출
                    token = jwtUtil.extractToken(authHeader);
                } else {
                    // Authorization 헤더가 없으면 OAuth 전용 쿠키에서 확인
                    token = getTokenFromOAuthCookie(request);
                }

                if (token == null || token.isEmpty()) {
                    return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
                }

                try {
                    currentUserId = (String) jwtUtil.getClaim(token, "mbrId");

                    // JWT 모드에서는 DB에서 최신 정보 조회
                    HashMap<String, Object> params = new HashMap<>();
                    params.put("mbrId", currentUserId);
                    mberInfo = mberService.findByMbr(params);

                    if (mberInfo == null) {
                        return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
                    }

                    // 응답 데이터 구성
                    HashMap<String, Object> result = new HashMap<>();
                    result.put("mbrId", mberInfo.getMbrId());
                    result.put("mbrNm", mberInfo.getMbrNm());
                    result.put("roles", mberInfo.getAuthorList());

                    // 토큰도 함께 전달
                    if (authHeader != null && !authHeader.isEmpty()) {
                        result.put("token", authHeader);
                    } else {
                        // 쿠키에서 가져온 토큰이면 Bearer 형태로 반환
                        String oauthToken = getTokenFromOAuthCookie(request);
                        if (oauthToken != null) {
                            result.put("token", "Bearer " + oauthToken);
                        }
                    }
                    return resUtil.successRes(result, MessageCode.COMMON_SUCCESS);

                } catch (IllegalArgumentException e) {
                    return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
                }
            }
            // 여기까지 왔다면 사용자를 찾을 수 없음
            return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);

        } catch (Exception e) {
            return resUtil.errorRes(MessageCode.COMMON_UNKNOWN_ERROR);
        }
    }

    /**
     * OAuth 전용 쿠키에서 access token 추출
     */
    private String getTokenFromOAuthCookie(HttpServletRequest request) {
        if (request.getCookies() != null) {
            for (Cookie cookie : request.getCookies()) {
                if ("oauth_access_token".equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }
        return null;
    }

    /**
     * 지원하는 OAuth 제공자 목록 조회 API
     */
    @GetMapping("/providers")
    public ResponseEntity<?> getSupportedProviders() {
        return resUtil.successRes(SUPPORTED_PROVIDERS, MessageCode.COMMON_SUCCESS);
    }

    /**
     * OAuth 제공자 유효성 검증
     */
    private void validateProvider(String provider) {
        if (provider == null || provider.trim().isEmpty()) {
            throw new IllegalArgumentException("OAuth 제공자가 지정되지 않았습니다.");
        }

        if (!SUPPORTED_PROVIDERS.contains(provider.toLowerCase())) {
            throw new IllegalArgumentException("지원하지 않는 OAuth 제공자입니다: " + provider);
        }
    }

    /**
     * 보안 검증 (필요시 확장)
     */
    private void validateSecurity(HttpServletRequest request) {
        String clientIP = httpRequestUtil.getIp(request);
        // 예시: 로컬 개발 환경이 아닌 경우 추가 검증
        if (!"127.0.0.1".equals(clientIP) && !"::1".equals(clientIP)) {
            // 운영 환경 보안 검증 로직
        }
    }

    /**
     * 에러 발생 시 프론트엔드로 리다이렉트
     */
    private void handleError(HttpServletResponse response, String errorCode, String errorMessage) throws IOException {
        String encodedMessage = java.net.URLEncoder.encode(errorMessage, "UTF-8");
        String errorUrl = String.format("%s/login.page?error=%s&message=%s", FRONT_URL, errorCode, encodedMessage);
        response.sendRedirect(errorUrl);
    }
}