package com.gcsc.connection.monitoring.service;

import com.gcsc.connection.common.exception.BusinessException;
import com.gcsc.connection.common.exception.ErrorCode;
import com.gcsc.connection.monitoring.dto.RequestLogResponse;
import com.gcsc.connection.monitoring.dto.RequestLogSearchDto;
import com.gcsc.connection.monitoring.entity.SnpApiRequestLog;
import com.gcsc.connection.monitoring.repository.SnpApiRequestLogRepository;
import com.gcsc.connection.monitoring.spec.RequestLogSpecification;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
@RequiredArgsConstructor
public class RequestLogService {

    private final SnpApiRequestLogRepository snpApiRequestLogRepository;

    /**
     * 비동기 요청 로그 저장
     */
    @Async("taskExecutor")
    @Transactional
    public void saveLogAsync(SnpApiRequestLog logEntry) {
        try {
            snpApiRequestLogRepository.save(logEntry);
        } catch (Exception e) {
            log.error("요청 로그 저장 실패", e);
        }
    }

    /**
     * 요청 로그 검색
     */
    @Transactional(readOnly = true)
    public Page<RequestLogResponse> searchLogs(RequestLogSearchDto searchDto) {
        Specification<SnpApiRequestLog> spec = RequestLogSpecification.withFilters(searchDto);
        Pageable pageable = PageRequest.of(searchDto.page(), searchDto.size());
        return snpApiRequestLogRepository.findAll(spec, pageable).map(RequestLogResponse::from);
    }

    /**
     * 요청 로그 상세 조회
     */
    @Transactional(readOnly = true)
    public RequestLogResponse getLogDetail(Long logId) {
        return snpApiRequestLogRepository.findById(logId)
            .map(RequestLogResponse::fromDetail)
            .orElseThrow(() -> new BusinessException(ErrorCode.INTERNAL_ERROR));
    }
}
