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.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 {
            // Provider 유효성 검증
            validateProvider(provider);

            // 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", "*");

            // 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();

            if ("S".equals(loginMode)) {
                // 세션 모드 처리
                return handleSessionModeUserInfo(request);
            } else {
                // JWT 모드 처리
                return handleJWTModeUserInfo(request);
            }

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

    /**
     * 세션 모드 사용자 정보 처리
     */
    private ResponseEntity<?> handleSessionModeUserInfo(HttpServletRequest request) {
        try {
            HttpSession session = request.getSession(false);
            if (session == null) {
                return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
            }

            String currentUserId = (String) session.getAttribute("mbrId");
            if (currentUserId == null) {
                return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
            }

            // 세션에서 정보 조회 및 최신 권한 정보 가져오기
            HashMap<String, Object> params = new HashMap<>();
            params.put("mbrId", currentUserId);
            MberVO 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());
            result.put("loginMode", "S");

            return resUtil.successRes(result, MessageCode.COMMON_SUCCESS);

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

    /**
     * JWT 모드 사용자 정보 처리
     */
    private ResponseEntity<?> handleJWTModeUserInfo(HttpServletRequest request) {
        try {
            String token = extractToken(request);
            if (token == null) {
                return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
            }

            // 토큰에서 사용자 ID 추출
            String currentUserId;
            try {
                currentUserId = (String) jwtUtil.getClaim(token, "mbrId");
                if (currentUserId == null || currentUserId.isEmpty()) {
                    return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
                }
            } catch (Exception e) {
                return resUtil.errorRes(MessageCode.LOGIN_USER_NOT_FOUND);
            }

            // DB에서 최신 사용자 정보 조회
            HashMap<String, Object> params = new HashMap<>();
            params.put("mbrId", currentUserId);
            MberVO 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());
            result.put("loginMode", "J");

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

            return resUtil.successRes(result, MessageCode.COMMON_SUCCESS);

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


    /**
     * 토큰 추출 로직 통합 및 개선
     */
    private String extractToken(HttpServletRequest request) {
        // Authorization 헤더에서 토큰 추출 시도
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && !authHeader.isEmpty()) {
            return jwtUtil.extractToken(authHeader);
        }

        // OAuth 전용 쿠키에서 토큰 추출 시도
        String oauthToken = getTokenFromOAuthCookie(request);
        if (oauthToken != null && !oauthToken.isEmpty()) {
            return oauthToken;
        }

        // 일반 Authorization 쿠키에서 토큰 추출 시도
        if (request.getCookies() != null) {
            for (Cookie cookie : request.getCookies()) {
                if ("Authorization".equals(cookie.getName())) {
                    return jwtUtil.extractToken(cookie.getValue());
                }
            }
        }

        return null;
    }

    /**
     * OAuth 전용 쿠키에서 access token 추출
     */
    private String getTokenFromOAuthCookie(HttpServletRequest request) {
        if (request.getCookies() != null) {
            for (Cookie cookie : request.getCookies()) {
                if ("Authorization".equals(cookie.getName()) ||
                        "refresh".equals(cookie.getName())) {
                    String token = cookie.getValue();
                    return token.startsWith("Bearer ") ? token.substring(7) : token;
                }
            }
        }
        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 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);
    }
}