package gc.mda.kcg.auth;

import gc.mda.kcg.audit.AuditLog;
import gc.mda.kcg.audit.AuditLogRepository;
import gc.mda.kcg.auth.provider.AuthProvider;
import gc.mda.kcg.auth.provider.PasswordAuthProvider;
import gc.mda.kcg.permission.PermissionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 인증 + 로그인 이력/감사 기록.
 * 로그인 이력 기록은 LoginAuditWriter (REQUIRES_NEW 트랜잭션)에 위임 — 실패 시에도 기록 보존.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthService {

    private final PasswordAuthProvider passwordAuthProvider;
    private final UserRepository userRepository;
    private final AuditLogRepository auditLogRepository;
    private final PermissionService permissionService;
    private final LoginAuditWriter loginAuditWriter;

    /**
     * ID/PW 로그인.
     * 트랜잭션을 별도 분리: 인증 실패가 외부 호출자(Controller)에서 catch되더라도
     * LoginAuditWriter는 REQUIRES_NEW로 별도 커밋되어 기록이 남는다.
     */
    public AuthResult login(String userAcnt, String password, String ipAddress, String userAgent) {
        AuthProvider.AuthRequest req = new AuthProvider.AuthRequest(userAcnt, password, ipAddress, userAgent);

        try {
            User user = passwordAuthProvider.authenticate(req);
            loginAuditWriter.recordSuccess(user.getUserId(), user.getUserAcnt(), ipAddress, userAgent);
            return AuthResult.success(user);
        } catch (AuthProvider.AuthenticationException e) {
            loginAuditWriter.recordFailure(userAcnt, ipAddress, userAgent, e.getReason());
            throw e;
        }
    }

    /**
     * 로그아웃 - 감사로그만 기록.
     */
    @Transactional
    public void logout(UUID userId, String userAcnt, String ipAddress) {
        auditLogRepository.save(AuditLog.builder()
                .userId(userId)
                .userAcnt(userAcnt)
                .actionCd("LOGOUT")
                .resourceType("SYSTEM")
                .resourceId("auth")
                .ipAddress(ipAddress)
                .result("SUCCESS")
                .build());
    }

    @Transactional(readOnly = true)
    public UserInfo getUserInfo(UUID userId) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalStateException("User not found: " + userId));
        List<String> roles = permissionService.getRoleCodesByUserId(userId);
        Map<String, List<String>> perms = permissionService.getResolvedPermissionsByUserId(userId);
        return new UserInfo(user, roles, perms);
    }

    public record AuthResult(User user) {
        public static AuthResult success(User user) { return new AuthResult(user); }
    }

    public record UserInfo(User user, List<String> roles, Map<String, List<String>> permissions) {}
}
