package go.kr.project.login.controller;

import egovframework.configProperties.LoginProperties;
import egovframework.constant.MessageConstants;
import egovframework.constant.TilesConstants;
import egovframework.exception.MessageException;
import egovframework.util.ApiResponseUtil;
import go.kr.project.login.model.SessionVO;
import go.kr.project.login.service.LoginService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * packageName    : go.kr.project.login.controller
 * fileName       : LoginController
 * author         : 박성영
 * date           : 25. 5. 8.
 * description    : 로그인 관련 컨트롤러
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 25. 5. 8.        박성영       최초 생성
 * 25. 5. 13.       시스템 관리자   로그인/로그아웃 기능 추가
 * 25. 5. 14.       시스템 관리자   ApiResponseUtil 적용 및 ajax 엔드포인트 추가
 */
@RequestMapping("/login")
@Controller
@RequiredArgsConstructor
@Slf4j
@Tag(name = "0. 인증", description = "로그인/로그아웃 등 인증 관련 API")
public class LoginController {

    private final LoginService loginService;

    private final LoginProperties loginProperties;

    /**
     * 로그인 페이지로 이동
     * @param model 뷰에 전달할 데이터를 담는 모델 객체
     * @param request HTTP 요청 객체
     * @return 로그인 페이지 뷰
     */
    @Operation(summary = "로그인 페이지 조회", description = "로그인 페이지를 조회합니다.")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "로그인 페이지 조회 성공")
    })
    @GetMapping(value = "/login.do")
    public String loginPage(Model model, HttpServletRequest request) {
        // 쿠키에서 저장된 아이디 가져오기
        Cookie[] cookies = request.getCookies();
        String savedUserId = "";
        boolean isSaveId = false;

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("savedUserId".equals(cookie.getName())) {
                    try {
                        // URL 디코딩하여 원래 값으로 복원
                        savedUserId = java.net.URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.toString());
                        isSaveId = true;
                    } catch (Exception e) {
                        // 디코딩 실패 시 원래 값 사용
                        savedUserId = cookie.getValue();
                        isSaveId = true;
                    }
                    break;
                }
            }
        }

        model.addAttribute("savedUserId", savedUserId);
        model.addAttribute("isSaveId", isSaveId);

        return "login/login"+ TilesConstants.LOGIN;
    }

    /**
     * 로그인 처리 (AJAX)
     * 공통 응답 형식(ApiResponse)을 사용하여 응답합니다.
     * 
     * @param userAcnt 사용자 계정
     * @param passwd 비밀번호
     * @param saveId 아이디 저장 여부
     * @param request HTTP 요청 객체
     * @param response HTTP 응답 객체
     * @return 로그인 결과와 성공 상태를 담은 ResponseEntity 객체
     */
    @Operation(summary = "로그인 처리", description = "사용자 로그인을 처리하고 결과를 반환합니다.")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "로그인 성공"),
        @ApiResponse(responseCode = "400", description = "로그인 실패"),
        @ApiResponse(description = "오류로 인한 실패")
    })
    @PostMapping("/login.ajax")
    public ResponseEntity<?> loginAjax(
            @RequestParam("userAcnt") String userAcnt,
            @RequestParam("passwd") String passwd,
            @RequestParam(value = "saveId", required = false) String saveId,
            HttpServletRequest request,
            HttpServletResponse response) {

        try {
            // 로그인 처리
            SessionVO sessionVO = loginService.login(userAcnt, passwd, request, response);

            if (sessionVO != null) {
                //비활성화 계정 로그인 취소
                if( !"ACTIVE".equals(sessionVO.getUser().getUserStatusCd())
                        && "INACTIVE".equals(sessionVO.getUser().getUserStatusCd()) ){
                    return ApiResponseUtil.error(MessageConstants.Login.ERROR_INACTIVE_ACCOUNT);
                }

                // 아이디 저장 처리
                if ("Y".equals(saveId)) {
                    // 아이디 저장 쿠키 생성 (유효기간 7일)
                    // HTTP 응답 분할 취약점 방지를 위해 URLEncoder.encode 사용
                    String encodedUserAcnt = URLEncoder.encode(userAcnt, StandardCharsets.UTF_8.toString());
                    Cookie cookie = new Cookie("savedUserId", encodedUserAcnt);
                    cookie.setMaxAge(60 * 60 * 24 * 7);
                    cookie.setPath("/");
                    cookie.setHttpOnly(true); // XSS 공격 방지
                    cookie.setSecure(request.isSecure()); // HTTPS 연결에서만 쿠키 전송
                    response.addCookie(cookie);
                } else {
                    // 아이디 저장 쿠키 삭제
                    Cookie cookie = new Cookie("savedUserId", "");
                    cookie.setMaxAge(0);
                    cookie.setPath("/");
                    cookie.setHttpOnly(true); // XSS 공격 방지
                    cookie.setSecure(request.isSecure()); // HTTPS 연결에서만 쿠키 전송
                    response.addCookie(cookie);
                }

                // 비밀번호 초기화 여부 확인
                if ("Y".equals(sessionVO.getUser().getPasswdInitYn())) {
                    // 비밀번호 초기화된 경우 비밀번호 변경 페이지로 리다이렉트
                    Map<String, Object> data = new HashMap<>();
                    data.put("redirectUrl", "/mypage/password-reset.do");
                    data.put("passwordInitialized", true);
                    return ApiResponseUtil.success(data, "비밀번호를 변경해야 합니다.");
                } else {
                    // 로그인 성공
                    Map<String, Object> data = new HashMap<>();
                    data.put("redirectUrl", "/");
                    return ApiResponseUtil.success(data, MessageConstants.Login.SUCCESS);
                }
            } else {
                // 로그인 실패
                return ApiResponseUtil.error(MessageConstants.Login.ERROR_INVALID_CREDENTIALS);
            }
        } catch (MessageException e1) {
            log.error("사용자 계점 잠김", e1);
            return ApiResponseUtil.error(e1.getMessage());
        } catch (Exception e2) {
            log.error("로그인 처리 중 오류 발생", e2);
            return ApiResponseUtil.error(MessageConstants.Login.ERROR_PROCESS);
        }
    }

    /**
     * 로그아웃 처리
     * @param request HTTP 요청 객체
     * @param response HTTP 응답 객체
     * @return 로그아웃 후 리다이렉트 페이지
     */
    @Operation(summary = "로그아웃 처리", description = "사용자 로그아웃을 처리하고 로그인 페이지로 리다이렉트합니다.")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "로그아웃 성공")
    })
    @GetMapping("/logout.do")
    public String logout(HttpServletRequest request, HttpServletResponse response) {
        try {
            loginService.logout(request, response);
        } catch (Exception e) {
            log.error("로그아웃 처리 중 오류 발생", e);
        }
        return "redirect:" + loginProperties.getUrl();
    }

    /**
     * 로그아웃 처리 (AJAX)
     * 공통 응답 형식(ApiResponse)을 사용하여 응답합니다.
     * 
     * @param request HTTP 요청 객체
     * @param response HTTP 응답 객체
     * @return 로그아웃 결과와 성공 상태를 담은 ResponseEntity 객체
     */
    @Operation(summary = "로그아웃 처리 (AJAX)", description = "사용자 로그아웃을 처리하고 결과를 JSON으로 반환합니다.")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "로그아웃 성공"),
        @ApiResponse(responseCode = "400", description = "로그아웃 실패"),
        @ApiResponse(description = "오류로 인한 실패")
    })
    @PostMapping("/logout.ajax")
    public ResponseEntity<?> logoutAjax(HttpServletRequest request, HttpServletResponse response) {
        try {
            loginService.logout(request, response);
            Map<String, Object> data = new HashMap<>();
            data.put("redirectUrl", loginProperties.getUrl());
            return ApiResponseUtil.success(data, MessageConstants.Logout.SUCCESS);
        } catch (Exception e) {
            log.error("로그아웃 처리 중 오류 발생", e);
            return ApiResponseUtil.error(MessageConstants.Logout.ERROR_PROCESS);
        }
    }

    /**
     * 계정 잠금 해제
     * 공통 응답 형식(ApiResponse)을 사용하여 응답합니다.
     * 
     * @param userId 사용자 ID
     * @return 잠금 해제 결과와 성공 상태를 담은 ResponseEntity 객체
     */
    @Operation(summary = "계정 잠금 해제", description = "비밀번호 오류로 잠긴 계정의 잠금을 해제합니다.")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "계정 잠금 해제 성공"),
        @ApiResponse(responseCode = "400", description = "계정 잠금 해제 실패"),
        @ApiResponse(description = "오류로 인한 실패")
    })
    @PostMapping("/unlockAccount.ajax")
    public ResponseEntity<?> unlockAccountAjax(@RequestParam("userId") String userId) {
        try {
            boolean result = loginService.unlockPassword(userId);

            if (result) {
                return ApiResponseUtil.success(MessageConstants.Account.UNLOCK_SUCCESS);
            } else {
                return ApiResponseUtil.error(MessageConstants.Account.UNLOCK_FAIL);
            }
        } catch (Exception e) {
            log.error("계정 잠금 해제 중 오류 발생", e);
            return ApiResponseUtil.error(MessageConstants.Account.UNLOCK_ERROR);
        }
    }

}
