package incheon.com.security.service.impl;

import incheon.com.security.mapper.SecurityUserMapper;
import incheon.com.security.service.SecurityUserService;
import incheon.com.security.vo.LoginVO;
import incheon.com.security.vo.UserAuthrtVO;
import incheon.com.security.vo.UserRoleVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 보안 인증용 사용자 서비스 구현체
 * - 외부 의존성 없이 Security 자체 완결형 구조
 * - 인증/인가에 필요한 최소한의 기능만 제공
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SecurityUserServiceImpl implements SecurityUserService {
    
    private final SecurityUserMapper securityUserMapper;
    
    @Override
    public LoginVO getUserById(String userId) {
        try {
            log.info("사용자 조회 시작: {}", userId);
            
            LoginVO loginVO = securityUserMapper.selectUserForLogin(userId);
            if (loginVO == null) {
                log.warn("사용자를 찾을 수 없습니다: {}", userId);
                return null;
            }
            
            log.info("사용자 조회 성공: {} ({})", loginVO.getUserNm(), loginVO.getUserId());
            return loginVO;
            
        } catch (Exception e) {
            log.error("사용자 조회 중 오류 발생: {}", userId, e);
            return null;
        }
    }
    
    @Override
    public List<LoginVO> getAllActiveUsers() {
        try {
            log.info("활성 사용자 목록 조회 시작");
            
            List<LoginVO> loginUsers = securityUserMapper.selectActiveUsers();
            log.info("활성 사용자 조회 완료: {} 명", loginUsers != null ? loginUsers.size() : 0);
            
            return loginUsers;
            
        } catch (Exception e) {
            log.error("활성 사용자 목록 조회 중 오류 발생", e);
            return List.of(); // 빈 리스트 반환
        }
    }
    
    @Override
    public String getUserRole(String userId, String deptCd, String jbgdCd) {
        try {
            // DB에서 직접 권한 조회 (SQL에서 계산된 결과)
            String role = securityUserMapper.selectUserRole(userId);
            return role != null ? role : "ROLE_USER"; // 기본값

        } catch (Exception e) {
            log.error("사용자 권한 조회 중 오류 발생: {}", userId, e);
            return "ROLE_USER"; // 기본값
        }
    }

    @Override
    public LoginVO getUserWithAuthrts(String userId) {
        try {
            log.info("사용자 권한 정보 조회 시작: {}", userId);

            // 1. 기본 사용자 정보 조회
            LoginVO loginVO = securityUserMapper.selectUserForLogin(userId);
            if (loginVO == null) {
                log.warn("사용자를 찾을 수 없습니다: {}", userId);
                return null;
            }

            // 2. 권한 정보 조회 (시스템별)
            List<UserAuthrtVO> userAuthrts = securityUserMapper.selectUserAuthrts(userId);
            loginVO.setUserAuthrts(userAuthrts);

            // 3. 역할 정보 조회
            List<UserRoleVO> userRoles = securityUserMapper.selectUserRoles(userId);
            List<UserRoleVO> tempUserRoles = securityUserMapper.selectTempUserRoles(userId);
            loginVO.setUserRoles(userRoles);
            loginVO.setTempUserRoles(tempUserRoles);

            // 4. Spring Security용 GrantedAuthority 변환
            // 형식: "시스템코드:권한코드" (예: "AGS:PERM_FUNC_EDIT")
            List<GrantedAuthority> authorities = userAuthrts.stream()
                    .map(authrt -> new SimpleGrantedAuthority(authrt.toAuthority()))
                    .distinct()
                    .collect(Collectors.toList());
            loginVO.setAuthorities(authorities);

            log.info("사용자 권한 로딩 완료: {} ({} 권한, {} 정규역할, {} 임시역할)",
                    loginVO.getUserNm(),
                    authorities.size(),
                    userRoles.size(),
                    tempUserRoles.size());

            // 5. 접근 가능한 시스템 코드 목록 추출 및 저장 (세션 캐싱용)
            List<String> activeSysCds = userAuthrts.stream()
                    .map(UserAuthrtVO::getSysCd)
                    .distinct()
                    .sorted()
                    .collect(Collectors.toList());
            loginVO.setActiveSysCds(activeSysCds);
            log.info("접근 가능한 시스템: {}", activeSysCds);

            // 6. 통합관리자 여부 판별 (ROLTYP001 역할유형 보유 여부)
            boolean isSuperAdmin = userRoles.stream()
                    .anyMatch(r -> "ROLTYP001".equals(r.getRoleTypeCd()));
            loginVO.setSuperAdmin(isSuperAdmin);
            log.info("통합관리자 여부: {}", isSuperAdmin);

            // 7. 업무관리자 시스템 목록 (ROLTYP002 역할의 시스템)
            List<String> adminSysCds = userRoles.stream()
                    .filter(r -> "ROLTYP002".equals(r.getRoleTypeCd()))
                    .map(UserRoleVO::getSysCd)
                    .filter(sysCd -> sysCd != null)
                    .distinct()
                    .sorted()
                    .collect(Collectors.toList());
            loginVO.setAdminSysCds(adminSysCds);
            log.info("업무관리자 시스템: {}", adminSysCds);

            return loginVO;

        } catch (Exception e) {
            log.error("사용자 권한 조회 중 오류 발생: {}", userId, e);
            return null;
        }
    }

    @Override
    public void updateUserSession(String userId) {
        try {
            log.info("사용자 세션 정보 업데이트 (로그인 시간): {}", userId);
            LoginVO loginVO = new LoginVO();
            loginVO.setUserId(userId);
            securityUserMapper.updateUserSession(loginVO);
        } catch (Exception e) {
            log.error("사용자 세션 정보 업데이트 중 오류 발생: {}", userId, e);
        }
    }

    @Override
    public boolean refreshSessionAuthrts(HttpSession session, String userId) {
        try {
            log.info("세션 권한 정보 갱신 시작: {}", userId);

            // 1. 현재 세션에서 기존 LoginVO 가져오기
            LoginVO currentUser = (LoginVO) session.getAttribute("loginVO");
            if (currentUser == null) {
                log.warn("세션에 사용자 정보가 없습니다: {}", userId);
                return false;
            }

            // 2. DB에서 권한 정보 다시 조회
            List<UserAuthrtVO> userAuthrts = securityUserMapper.selectUserAuthrts(userId);
            List<UserRoleVO> userRoles = securityUserMapper.selectUserRoles(userId);
            List<UserRoleVO> tempUserRoles = securityUserMapper.selectTempUserRoles(userId);

            // 3. GrantedAuthority 변환
            List<GrantedAuthority> authorities = userAuthrts.stream()
                    .map(authrt -> new SimpleGrantedAuthority(authrt.toAuthority()))
                    .distinct()
                    .collect(Collectors.toList());

            // 4. 접근 가능한 시스템 코드 목록
            List<String> activeSysCds = userAuthrts.stream()
                    .map(UserAuthrtVO::getSysCd)
                    .distinct()
                    .sorted()
                    .collect(Collectors.toList());

            // 5. 통합관리자 여부 판별 (ROLTYP001 역할유형 보유 여부)
            boolean isSuperAdmin = userRoles.stream()
                    .anyMatch(r -> "ROLTYP001".equals(r.getRoleTypeCd()));

            // 6. 업무관리자 시스템 목록
            List<String> adminSysCds = userRoles.stream()
                    .filter(r -> "ROLTYP002".equals(r.getRoleTypeCd()))
                    .map(UserRoleVO::getSysCd)
                    .filter(sysCd -> sysCd != null)
                    .distinct()
                    .sorted()
                    .collect(Collectors.toList());

            // 7. 세션의 LoginVO 업데이트
            currentUser.setUserAuthrts(userAuthrts);
            currentUser.setUserRoles(userRoles);
            currentUser.setTempUserRoles(tempUserRoles);
            currentUser.setAuthorities(authorities);
            currentUser.setActiveSysCds(activeSysCds);
            currentUser.setSuperAdmin(isSuperAdmin);
            currentUser.setAdminSysCds(adminSysCds);

            // 세션에 다시 저장 (객체 참조라서 사실 불필요하지만 명시적으로)
            session.setAttribute("loginVO", currentUser);

            // 6. SecurityContextHolder 갱신
            Authentication newAuth = new UsernamePasswordAuthenticationToken(
                    currentUser,
                    null,
                    authorities
            );
            SecurityContextHolder.getContext().setAuthentication(newAuth);

            log.info("세션 권한 정보 갱신 완료: {} ({} 권한, {} 정규역할, {} 임시역할)",
                    userId, authorities.size(), userRoles.size(), tempUserRoles.size());

            return true;

        } catch (Exception e) {
            log.error("세션 권한 정보 갱신 중 오류 발생: {}", userId, e);
            return false;
        }
    }
}
