package incheon.com.cmm.exception;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import incheon.com.cmm.api.DefaultApiResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName : CustomErrorController.java
 * @Description : 스프링 부트 오류 페이지 컨트롤러
 *
 * @author : 관리자
 * @since  : 2023. 10. 10
 * @version : 1.0
 *
 * <pre>
 * << 개정이력(Modification Information) >>
 *
 *   수정일              수정자               수정내용
 *  -------------  ------------   ---------------------
 *   2023. 10. 10    관리자               최초 생성
 *   2025.10. 2      AI                 개발 환경용 상세 오류 정보 추가
 * </pre>
 *
 */
@Slf4j
@Controller
public class CustomErrorController implements ErrorController {

    @Value("${spring.profiles.active:local}")
    private String activeProfile;

    /**
     * 에러 페이지 핸들링
     * @param request HTTP 요청 객체
     * @param model 모델 객체
     * @return 적절한 오류 페이지 뷰 이름 또는 API 응답
     */
    @RequestMapping("/error")
    public Object handleError(HttpServletRequest request, Model model) {
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        String requestUri = (String) request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);

        // REST API 요청인지 확인
        if (isRestApiRequest(request)) {
            return handleApiError(request, status, requestUri);
        }

        // HTML 요청 처리 (기존 로직)
        if (status == null) {
            return isDevelopmentMode() ? "error/error-dev" : "error/error";
        }

        int statusCode = Integer.parseInt(status.toString());
        log.error("오류 발생: Status={}, URI={}", statusCode, requestUri);

        model.addAttribute("requestUri", requestUri);
        model.addAttribute("statusCode", statusCode);

        // 개발 모드에서는 상세 정보 추가
        if (isDevelopmentMode()) {
            addDevelopmentModeAttributes(request, model);
        }

        if (statusCode == HttpStatus.BAD_REQUEST.value()) {
            model.addAttribute("errorType", "잘못된 요청");
            model.addAttribute("errorMessage", "요청 형식이 올바르지 않습니다. 입력값을 확인해주세요.");
            return isDevelopmentMode() ? "error/error-dev" : "error/error";
        } else if (statusCode == HttpStatus.NOT_FOUND.value()) {
            model.addAttribute("errorType", "페이지 찾기 실패");
            model.addAttribute("errorMessage", "요청하신 페이지를 찾을 수 없습니다.");
            return isDevelopmentMode() ? "error/error-dev" : "error/error";
        } else if (statusCode == HttpStatus.UNAUTHORIZED.value()) {
            // 401 에러는 로그인 페이지로 리다이렉트
            return "redirect:/?error=unauthorized";
        } else if (statusCode == HttpStatus.FORBIDDEN.value()) {
            model.addAttribute("errorType", "접근 권한 없음");
            model.addAttribute("errorMessage", "해당 리소스에 접근할 권한이 없습니다.");
            return isDevelopmentMode() ? "error/error-dev" : "error/error";
        } else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
        	Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
        	if (throwable != null) {
        		log.error("#####################################################");
        		log.error("#####################################################");
        		log.error("#####################################################");
        	    log.error("요청 처리 중 예외 발생", throwable);
        	    log.error("#####################################################");
        	    log.error("#####################################################");
        	    log.error("#####################################################");
        	}
            model.addAttribute("errorType", "서버 내부 오류");
            model.addAttribute("errorMessage", "서버 내부 처리 중 오류가 발생했습니다.");
            return isDevelopmentMode() ? "error/error-dev" : "error/error";
        }

        // 기타 에러
        model.addAttribute("errorType", "오류 발생");
        model.addAttribute("errorMessage", "처리 중 예상치 못한 오류가 발생했습니다.");
        return isDevelopmentMode() ? "error/error-dev" : "error/error";
    }

    /**
     * REST API 요청인지 확인
     */
    private boolean isRestApiRequest(HttpServletRequest request) {
        String requestUri = (String) request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
        String acceptHeader = request.getHeader("Accept");
        String contentType = request.getContentType();

        // 1. URI 패턴으로 확인 (Action.do는 API로 간주)
        if (requestUri != null && (requestUri.contains("Action.do") || requestUri.startsWith("/api/"))) {
            return true;
        }

        // 2. Accept 헤더로 확인
        if (acceptHeader != null &&
            (acceptHeader.contains(MediaType.APPLICATION_JSON_VALUE) ||
             acceptHeader.contains("application/json"))) {
            return true;
        }

        // 3. Content-Type 헤더로 확인
        if (contentType != null && contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
            return true;
        }

        // 4. XMLHttpRequest 확인
        String xRequestedWith = request.getHeader("X-Requested-With");
        if ("XMLHttpRequest".equals(xRequestedWith)) {
            return true;
        }

        return false;
    }

    /**
     * API 에러 응답 처리
     */
    private ResponseEntity<DefaultApiResponse<Object>> handleApiError(
            HttpServletRequest request, Object status, String requestUri) {

        int statusCode = status != null ? Integer.parseInt(status.toString()) : 500;
        Throwable exception = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

        String errorMessage = "서버 처리 중 오류가 발생했습니다.";
        String errorType = "ServerError";

        if (exception != null) {
            errorMessage = exception.getMessage() != null ? exception.getMessage() : errorMessage;
            errorType = exception.getClass().getSimpleName();
        }

        log.error("API 오류 발생: Status={}, URI={}, Exception={}", statusCode, requestUri, errorType);

        DefaultApiResponse<Object> response = DefaultApiResponse.error(
            statusCode,
            errorMessage,
            errorType
        );

        return new ResponseEntity<>(response, HttpStatus.valueOf(statusCode));
    }
    
    /**
     * 개발 모드 여부 확인
     */
    private boolean isDevelopmentMode() {
        return "local".equals(activeProfile) || "DEV".equals(activeProfile);
    }
    
    /**
     * 개발 모드용 상세 정보 추가
     */
    private void addDevelopmentModeAttributes(HttpServletRequest request, Model model) {
        // Exception 정보
        Throwable exception = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
        if (exception != null) {
            model.addAttribute("exception", exception);
            model.addAttribute("exceptionType", exception.getClass().getName());
            model.addAttribute("exceptionMessage", exception.getMessage());
            
            // 스택 트레이스
            StackTraceElement[] stackTrace = exception.getStackTrace();
            model.addAttribute("stackTrace", stackTrace);
        }
        
        // 요청 정보
        model.addAttribute("method", request.getMethod());
        model.addAttribute("queryString", request.getQueryString());
        model.addAttribute("remoteAddr", request.getRemoteAddr());
        model.addAttribute("userAgent", request.getHeader("User-Agent"));
        
        // 요청 헤더
        Map<String, String> headers = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                headers.put(headerName, request.getHeader(headerName));
            }
        }
        model.addAttribute("headers", headers);
        
        // 요청 파라미터
        Map<String, String[]> parameters = request.getParameterMap();
        model.addAttribute("parameters", parameters);
        
        // 세션 정보
        HttpSession session = request.getSession(false);
        if (session != null) {
            model.addAttribute("sessionId", session.getId());
            model.addAttribute("sessionCreationTime", new java.util.Date(session.getCreationTime()));
            model.addAttribute("loginUser", session.getAttribute("loginVO"));
        }
        
        // 서버 환경 정보
        model.addAttribute("activeProfile", activeProfile);
        model.addAttribute("serverTime", new java.util.Date());
        
        // JVM 메모리 정보
        Runtime runtime = Runtime.getRuntime();
        model.addAttribute("totalMemory", runtime.totalMemory() / 1024 / 1024 + " MB");
        model.addAttribute("freeMemory", runtime.freeMemory() / 1024 / 1024 + " MB");
        model.addAttribute("maxMemory", runtime.maxMemory() / 1024 / 1024 + " MB");
    }
} 