package com.gcsc.connection.apihub.service;

import com.gcsc.connection.apihub.dto.RecentApiResponse;
import com.gcsc.connection.apihub.dto.ServiceCatalogResponse;
import com.gcsc.connection.common.exception.BusinessException;
import com.gcsc.connection.common.exception.ErrorCode;
import com.gcsc.connection.service.entity.SnpApiDomain;
import com.gcsc.connection.service.entity.SnpService;
import com.gcsc.connection.service.entity.SnpServiceApi;
import com.gcsc.connection.service.repository.SnpApiDomainRepository;
import com.gcsc.connection.service.repository.SnpServiceApiRepository;
import com.gcsc.connection.service.repository.SnpServiceRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.gcsc.connection.monitoring.repository.SnpApiRequestLogRepository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class ApiHubService {

    private final SnpServiceRepository snpServiceRepository;
    private final SnpServiceApiRepository snpServiceApiRepository;
    private final SnpApiDomainRepository snpApiDomainRepository;
    private final SnpApiRequestLogRepository snpApiRequestLogRepository;

    /**
     * 활성 서비스와 각 서비스의 활성 API를 도메인별로 그룹화하여 카탈로그 반환
     */
    @Transactional(readOnly = true)
    public List<ServiceCatalogResponse> getCatalog() {
        Map<String, SnpApiDomain> domainMap = buildDomainMap();
        List<SnpService> activeServices = snpServiceRepository.findByIsActiveTrue();

        return activeServices.stream()
            .map(service -> {
                List<SnpServiceApi> activeApis = snpServiceApiRepository
                    .findByServiceServiceIdAndIsActiveTrue(service.getServiceId());
                return ServiceCatalogResponse.from(service, activeApis, domainMap);
            })
            .toList();
    }

    /**
     * 서비스 단건 카탈로그 조회
     */
    @Transactional(readOnly = true)
    public ServiceCatalogResponse getServiceCatalog(Long serviceId) {
        SnpService service = snpServiceRepository.findById(serviceId)
            .filter(SnpService::getIsActive)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        List<SnpServiceApi> activeApis = snpServiceApiRepository
            .findByServiceServiceIdAndIsActiveTrue(serviceId);

        Map<String, SnpApiDomain> domainMap = buildDomainMap();
        return ServiceCatalogResponse.from(service, activeApis, domainMap);
    }

    /**
     * 최근 등록된 활성 API 상위 10건 반환
     */
    @Transactional(readOnly = true)
    public List<RecentApiResponse> getRecentApis() {
        return snpServiceApiRepository.findTop10ByIsActiveTrueOrderByCreatedAtDesc().stream()
            .map(RecentApiResponse::from)
            .toList();
    }

    /**
     * 최근 1주일 기준 인기 API 상위 N건
     */
    @Transactional(readOnly = true)
    public List<com.gcsc.connection.apihub.dto.PopularApiResponse> getPopularApis(int limit) {
        LocalDateTime since = LocalDateTime.now().minusDays(7);
        return snpApiRequestLogRepository.findTopApisForHub(since, limit).stream()
            .map(row -> new com.gcsc.connection.apihub.dto.PopularApiResponse(
                (String) row[0],
                (String) row[1],
                row[2] != null ? ((Number) row[2]).longValue() : null,
                row[3] != null ? ((Number) row[3]).longValue() : null,
                ((Number) row[4]).longValue()
            ))
            .toList();
    }

    private Map<String, SnpApiDomain> buildDomainMap() {
        return snpApiDomainRepository.findAllByOrderBySortOrderAscDomainNameAsc().stream()
            .collect(Collectors.toMap(SnpApiDomain::getDomainName, Function.identity()));
    }
}
