package com.gcsc.connection.monitoring.repository;

import com.gcsc.connection.monitoring.entity.SnpApiRequestLog;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDateTime;
import java.util.List;

public interface SnpApiRequestLogRepository extends JpaRepository<SnpApiRequestLog, Long>,
    JpaSpecificationExecutor<SnpApiRequestLog> {

    /** API HUB 인기 API (최근 1주일, 도메인 포함) */
    @Query(value = "SELECT COALESCE(a.api_domain, '') as domain, " +
        "COALESCE(a.api_name, SPLIT_PART(l.request_url, '?', 1)) as apiName, " +
        "a.api_id, a.service_id, COUNT(*) as cnt " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "LEFT JOIN common.snp_service_api a ON s.service_id = a.service_id " +
        "AND a.api_path = SUBSTRING(SPLIT_PART(l.request_url, '?', 1) FROM '/gateway/[^/]+(.*)') " +
        "AND a.api_method = l.request_method " +
        "WHERE l.requested_at >= :since AND a.api_id IS NOT NULL " +
        "GROUP BY a.api_domain, a.api_name, a.api_id, a.service_id, SPLIT_PART(l.request_url, '?', 1) " +
        "ORDER BY cnt DESC LIMIT :limit", nativeQuery = true)
    List<Object[]> findTopApisForHub(@Param("since") LocalDateTime since, @Param("limit") int limit);

    /** API Key별 일일 요청 건수 */
    long countByApiKeyApiKeyIdAndRequestedAtGreaterThanEqual(Long apiKeyId, LocalDateTime startOfDay);

    /** 오늘 요약: 총 요청, 성공 건수, 평균 응답시간 */
    @Query(value = "SELECT COUNT(*) as total, " +
        "COUNT(CASE WHEN request_status = 'SUCCESS' THEN 1 END) as successCount, " +
        "COALESCE(AVG(response_time), 0) as avgResponseTime " +
        "FROM common.snp_api_request_log WHERE requested_at >= :startOfDay", nativeQuery = true)
    Object[] findTodayStats(@Param("startOfDay") LocalDateTime startOfDay);

    /** 전일 총 요청 건수 */
    @Query(value = "SELECT COUNT(*) FROM common.snp_api_request_log " +
        "WHERE requested_at >= :yesterday AND requested_at < :today", nativeQuery = true)
    long countYesterdayRequests(@Param("yesterday") LocalDateTime yesterday,
                                @Param("today") LocalDateTime today);

    /** 오늘 활성 사용자 수 */
    @Query(value = "SELECT COUNT(DISTINCT user_id) FROM common.snp_api_request_log " +
        "WHERE requested_at >= :startOfDay AND user_id IS NOT NULL", nativeQuery = true)
    long countTodayActiveUsers(@Param("startOfDay") LocalDateTime startOfDay);

    /** 시간별 요청 추이 (오늘) */
    @Query(value = "SELECT CAST(EXTRACT(HOUR FROM requested_at) AS int) as hour, " +
        "request_status as status, COUNT(*) as cnt " +
        "FROM common.snp_api_request_log WHERE requested_at >= :startOfDay " +
        "GROUP BY 1, 2 ORDER BY 1", nativeQuery = true)
    List<Object[]> findHourlyTrend(@Param("startOfDay") LocalDateTime startOfDay);

    /** 서비스별 요청 비율 */
    @Query(value = "SELECT s.service_name, COUNT(*) as cnt " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "WHERE l.requested_at >= :startOfDay " +
        "GROUP BY s.service_name ORDER BY cnt DESC", nativeQuery = true)
    List<Object[]> findServiceRatio(@Param("startOfDay") LocalDateTime startOfDay);

    /** 에러율 추이 (시간별) */
    @Query(value = "SELECT CAST(EXTRACT(HOUR FROM l.requested_at) AS int) as hour, " +
        "COALESCE(s.service_name, 'Unknown') as serviceName, " +
        "CAST(COUNT(CASE WHEN l.request_status != 'SUCCESS' THEN 1 END) AS float) " +
        "/ NULLIF(COUNT(*), 0) * 100 as errorRate " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "WHERE l.requested_at >= :startOfDay " +
        "GROUP BY 1, 2 ORDER BY 1", nativeQuery = true)
    List<Object[]> findErrorTrend(@Param("startOfDay") LocalDateTime startOfDay);

    /** 상위 API 랭킹 (service_name, api_name 포함, 쿼리스트링 제거) */
    @Query(value = "SELECT COALESCE(s.service_name, 'Unknown') as serviceName, " +
        "COALESCE(a.api_name, SPLIT_PART(l.request_url, '?', 1)) as apiName, " +
        "SPLIT_PART(l.request_url, '?', 1) as request_url, l.request_method, COUNT(*) as cnt " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "LEFT JOIN common.snp_service_api a ON s.service_id = a.service_id " +
        "AND a.api_path = SUBSTRING(SPLIT_PART(l.request_url, '?', 1) FROM '/gateway/[^/]+(.*)') " +
        "AND a.api_method = l.request_method " +
        "WHERE l.requested_at >= :startOfDay " +
        "GROUP BY s.service_name, a.api_name, SPLIT_PART(l.request_url, '?', 1), l.request_method " +
        "ORDER BY cnt DESC LIMIT :limit", nativeQuery = true)
    List<Object[]> findTopApis(@Param("startOfDay") LocalDateTime startOfDay,
                               @Param("limit") int limit);

    /** 테넌트별 요청 비율 */
    @Query(value = "SELECT COALESCE(t.tenant_name, 'Unknown') as tenantName, COUNT(*) as cnt " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_tenant t ON l.tenant_id = t.tenant_id " +
        "WHERE l.requested_at >= :startOfDay " +
        "GROUP BY t.tenant_name ORDER BY cnt DESC", nativeQuery = true)
    List<Object[]> findTenantRequestRatio(@Param("startOfDay") LocalDateTime startOfDay);

    /** 테넌트별 사용자 비율 */
    @Query(value = "SELECT COALESCE(t.tenant_name, 'Unknown') as tenantName, " +
        "COUNT(DISTINCT l.user_id) as userCount " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_tenant t ON l.tenant_id = t.tenant_id " +
        "WHERE l.requested_at >= :startOfDay AND l.user_id IS NOT NULL " +
        "GROUP BY t.tenant_name ORDER BY userCount DESC", nativeQuery = true)
    List<Object[]> findTenantUserRatio(@Param("startOfDay") LocalDateTime startOfDay);

    /** 최근 로그 20건 */
    List<SnpApiRequestLog> findTop20ByOrderByRequestedAtDesc();

    // ===== Statistics API 쿼리 =====

    /** 서비스별 요청 통계 (기간) */
    @Query(value = "SELECT s.service_id, s.service_name, COUNT(*), " +
        "COUNT(CASE WHEN l.request_status = 'SUCCESS' THEN 1 END), " +
        "AVG(l.response_time) " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end " +
        "GROUP BY s.service_id, s.service_name", nativeQuery = true)
    List<Object[]> findServiceRequestStats(@Param("start") LocalDateTime start,
                                           @Param("end") LocalDateTime end);

    /** 응답시간 느린 서비스 TOP 5 */
    @Query(value = "SELECT s.service_name, AVG(l.response_time), COUNT(*) " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end " +
        "AND l.response_time IS NOT NULL " +
        "GROUP BY s.service_name ORDER BY AVG(l.response_time) DESC LIMIT 5", nativeQuery = true)
    List<Object[]> findSlowestServices(@Param("start") LocalDateTime start,
                                       @Param("end") LocalDateTime end);

    /** 시간별 서비스별 요청 추이 */
    @Query(value = "SELECT CAST(EXTRACT(HOUR FROM l.requested_at) AS int), " +
        "COALESCE(s.service_name, 'Unknown'), COUNT(*) " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end " +
        "GROUP BY 1, 2 ORDER BY 1", nativeQuery = true)
    List<Object[]> findHourlyServiceTrend(@Param("start") LocalDateTime start,
                                          @Param("end") LocalDateTime end);

    /** 요청 많은 사용자 TOP 10 */
    @Query(value = "SELECT u.user_id, u.user_name, u.role, COUNT(*), " +
        "CAST(COUNT(CASE WHEN l.request_status = 'SUCCESS' THEN 1 END) AS float) " +
        "/ NULLIF(COUNT(*), 0) * 100 " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_user u ON l.user_id = u.user_id " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end AND l.user_id IS NOT NULL " +
        "GROUP BY u.user_id, u.user_name, u.role ORDER BY COUNT(*) DESC LIMIT 10", nativeQuery = true)
    List<Object[]> findTopUsersByRequestCount(@Param("start") LocalDateTime start,
                                              @Param("end") LocalDateTime end);

    /** 역할별 사용자/요청 분포 */
    @Query(value = "SELECT u.role, COUNT(DISTINCT l.user_id), COUNT(*) " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_user u ON l.user_id = u.user_id " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end AND l.user_id IS NOT NULL " +
        "GROUP BY u.role", nativeQuery = true)
    List<Object[]> findUserRoleDistribution(@Param("start") LocalDateTime start,
                                            @Param("end") LocalDateTime end);

    /** 일별 활성 사용자 수 */
    @Query(value = "SELECT CAST(DATE(l.requested_at) AS text), COUNT(DISTINCT l.user_id) " +
        "FROM common.snp_api_request_log l " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end AND l.user_id IS NOT NULL " +
        "GROUP BY DATE(l.requested_at) ORDER BY 1", nativeQuery = true)
    List<Object[]> findDailyActiveUsers(@Param("start") LocalDateTime start,
                                        @Param("end") LocalDateTime end);

    /** 기간 내 활성 사용자 수 (고유) */
    @Query(value = "SELECT COUNT(DISTINCT l.user_id) FROM common.snp_api_request_log l " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end AND l.user_id IS NOT NULL",
        nativeQuery = true)
    long countActiveUsers(@Param("start") LocalDateTime start,
                          @Param("end") LocalDateTime end);

    /** 상위 API 랭킹 (기간, 성공률/응답시간 포함, 쿼리파라미터 제외) */
    @Query(value = "SELECT COALESCE(s.service_name, 'Unknown'), " +
        "COALESCE(a.api_name, SPLIT_PART(l.request_url, '?', 1)), " +
        "SPLIT_PART(l.request_url, '?', 1), l.request_method, COUNT(*), " +
        "AVG(l.response_time), " +
        "CAST(COUNT(CASE WHEN l.request_status = 'SUCCESS' THEN 1 END) AS float) " +
        "/ NULLIF(COUNT(*), 0) * 100 " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "LEFT JOIN common.snp_service_api a ON s.service_id = a.service_id " +
        "AND a.api_path = SUBSTRING(SPLIT_PART(l.request_url, '?', 1) FROM '/gateway/[^/]+(.*)') " +
        "AND a.api_method = l.request_method " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end " +
        "GROUP BY s.service_name, a.api_name, SPLIT_PART(l.request_url, '?', 1), l.request_method " +
        "ORDER BY COUNT(*) DESC LIMIT 20", nativeQuery = true)
    List<Object[]> findTopApisWithStats(@Param("start") LocalDateTime start,
                                        @Param("end") LocalDateTime end);

    /** 에러 많은 API TOP 10 (쿼리파라미터 제외) */
    @Query(value = "SELECT COALESCE(s.service_name, 'Unknown'), " +
        "COALESCE(a.api_name, SPLIT_PART(l.request_url, '?', 1)), SPLIT_PART(l.request_url, '?', 1), " +
        "COUNT(CASE WHEN l.request_status != 'SUCCESS' THEN 1 END), " +
        "COUNT(*), " +
        "CAST(COUNT(CASE WHEN l.request_status != 'SUCCESS' THEN 1 END) AS float) " +
        "/ NULLIF(COUNT(*), 0) * 100 " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_service s ON l.service_id = s.service_id " +
        "LEFT JOIN common.snp_service_api a ON s.service_id = a.service_id " +
        "AND a.api_path = SUBSTRING(SPLIT_PART(l.request_url, '?', 1) FROM '/gateway/[^/]+(.*)') " +
        "AND a.api_method = l.request_method " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end " +
        "GROUP BY 1, 2, 3 ORDER BY 4 DESC LIMIT 10", nativeQuery = true)
    List<Object[]> findTopErrorApis(@Param("start") LocalDateTime start,
                                    @Param("end") LocalDateTime end);

    /** HTTP 메서드별 요청 분포 */
    @Query(value = "SELECT request_method, COUNT(*) " +
        "FROM common.snp_api_request_log " +
        "WHERE requested_at >= :start AND requested_at < :end " +
        "GROUP BY request_method ORDER BY COUNT(*) DESC", nativeQuery = true)
    List<Object[]> findMethodDistribution(@Param("start") LocalDateTime start,
                                          @Param("end") LocalDateTime end);

    /** HTTP 상태코드별 요청 분포 */
    @Query(value = "SELECT response_status, COUNT(*) " +
        "FROM common.snp_api_request_log " +
        "WHERE requested_at >= :start AND requested_at < :end AND response_status IS NOT NULL " +
        "GROUP BY response_status ORDER BY COUNT(*) DESC", nativeQuery = true)
    List<Object[]> findStatusCodeDistribution(@Param("start") LocalDateTime start,
                                              @Param("end") LocalDateTime end);

    /** 테넌트별 요청 통계 */
    @Query(value = "SELECT t.tenant_id, t.tenant_name, COUNT(*), COUNT(DISTINCT l.user_id), " +
        "CAST(COUNT(CASE WHEN l.request_status = 'SUCCESS' THEN 1 END) AS float) " +
        "/ NULLIF(COUNT(*), 0) * 100, " +
        "AVG(l.response_time) " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_tenant t ON l.tenant_id = t.tenant_id " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end " +
        "GROUP BY t.tenant_id, t.tenant_name", nativeQuery = true)
    List<Object[]> findTenantRequestStats(@Param("start") LocalDateTime start,
                                          @Param("end") LocalDateTime end);

    /** 일별 테넌트별 요청 추이 */
    @Query(value = "SELECT CAST(DATE(l.requested_at) AS text), " +
        "COALESCE(t.tenant_name, 'Unknown'), COUNT(*) " +
        "FROM common.snp_api_request_log l " +
        "LEFT JOIN common.snp_tenant t ON l.tenant_id = t.tenant_id " +
        "WHERE l.requested_at >= :start AND l.requested_at < :end " +
        "GROUP BY 1, 2 ORDER BY 1", nativeQuery = true)
    List<Object[]> findDailyTenantTrend(@Param("start") LocalDateTime start,
                                        @Param("end") LocalDateTime end);

    // ===== Usage Trend 쿼리 =====

    /** 일별 사용량 추이 (최근 30일) */
    @Query(value = "SELECT DATE(requested_at) as day, COUNT(*) as total, " +
        "COUNT(CASE WHEN request_status = 'SUCCESS' THEN 1 END) as success, " +
        "COALESCE(AVG(response_time), 0) as avg_rt, " +
        "COUNT(DISTINCT user_id) as users " +
        "FROM common.snp_api_request_log " +
        "WHERE requested_at >= :since " +
        "GROUP BY DATE(requested_at) ORDER BY day", nativeQuery = true)
    List<Object[]> findDailyUsageTrend(@Param("since") LocalDateTime since);

    /** 주별 사용량 추이 (최근 12주) */
    @Query(value = "SELECT CAST(DATE_TRUNC('week', requested_at) AS date) as week_start, COUNT(*) as total, " +
        "COUNT(CASE WHEN request_status = 'SUCCESS' THEN 1 END) as success, " +
        "COALESCE(AVG(response_time), 0) as avg_rt, " +
        "COUNT(DISTINCT user_id) as users " +
        "FROM common.snp_api_request_log " +
        "WHERE requested_at >= :since " +
        "GROUP BY DATE_TRUNC('week', requested_at) ORDER BY week_start", nativeQuery = true)
    List<Object[]> findWeeklyUsageTrend(@Param("since") LocalDateTime since);

    /** 월별 사용량 추이 (최근 12개월) */
    @Query(value = "SELECT CAST(DATE_TRUNC('month', requested_at) AS date) as month_start, COUNT(*) as total, " +
        "COUNT(CASE WHEN request_status = 'SUCCESS' THEN 1 END) as success, " +
        "COALESCE(AVG(response_time), 0) as avg_rt, " +
        "COUNT(DISTINCT user_id) as users " +
        "FROM common.snp_api_request_log " +
        "WHERE requested_at >= :since " +
        "GROUP BY DATE_TRUNC('month', requested_at) ORDER BY month_start", nativeQuery = true)
    List<Object[]> findMonthlyUsageTrend(@Param("since") LocalDateTime since);
}
