package com.gcsc.connection.statistics.service;

import com.gcsc.connection.apikey.repository.SnpApiKeyRepository;
import com.gcsc.connection.monitoring.repository.SnpApiRequestLogRepository;
import com.gcsc.connection.user.repository.SnpUserRepository;
import com.gcsc.connection.statistics.dto.ApiStatsResponse;
import com.gcsc.connection.statistics.dto.ApiStatsResponse.ApiCallRank;
import com.gcsc.connection.statistics.dto.ApiStatsResponse.ApiErrorRank;
import com.gcsc.connection.statistics.dto.ApiStatsResponse.ApiMethodDistribution;
import com.gcsc.connection.statistics.dto.ApiStatsResponse.HttpStatusDistribution;
import com.gcsc.connection.statistics.dto.ServiceStatsResponse;
import com.gcsc.connection.statistics.dto.ServiceStatsResponse.HourlyServiceTrend;
import com.gcsc.connection.statistics.dto.ServiceStatsResponse.ServiceRequestStats;
import com.gcsc.connection.statistics.dto.ServiceStatsResponse.ServiceResponseTimeRank;
import com.gcsc.connection.statistics.dto.TenantStatsResponse;
import com.gcsc.connection.statistics.dto.TenantStatsResponse.DailyTenantTrend;
import com.gcsc.connection.statistics.dto.TenantStatsResponse.TenantApiKeyStats;
import com.gcsc.connection.statistics.dto.TenantStatsResponse.TenantRequestStats;
import com.gcsc.connection.statistics.dto.UsageTrendResponse;
import com.gcsc.connection.statistics.dto.UserStatsResponse;
import com.gcsc.connection.statistics.dto.UserStatsResponse.DailyActiveUsers;
import com.gcsc.connection.statistics.dto.UserStatsResponse.UserRequestRank;
import com.gcsc.connection.statistics.dto.UserStatsResponse.UserRoleDistribution;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 통계 서비스 - 서비스/사용자/API/테넌트별 통계 데이터 제공
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class StatisticsService {

    private final SnpApiRequestLogRepository requestLogRepository;
    private final SnpApiKeyRepository apiKeyRepository;
    private final SnpUserRepository snpUserRepository;

    /**
     * 서비스별 통계 조회
     */
    @Transactional(readOnly = true)
    public ServiceStatsResponse getServiceStats(LocalDateTime start, LocalDateTime end) {
        List<ServiceRequestStats> serviceStats = requestLogRepository
            .findServiceRequestStats(start, end).stream()
            .map(row -> {
                long total = ((Number) row[2]).longValue();
                long success = ((Number) row[3]).longValue();
                double successRate = total > 0 ? ((double) success / total) * 100 : 0.0;
                double avgResponseTime = row[4] != null ? ((Number) row[4]).doubleValue() : 0.0;
                return new ServiceRequestStats(
                    row[0] != null ? ((Number) row[0]).longValue() : null,
                    (String) row[1],
                    total,
                    success,
                    successRate,
                    avgResponseTime
                );
            })
            .toList();

        List<ServiceResponseTimeRank> slowestServices = requestLogRepository
            .findSlowestServices(start, end).stream()
            .map(row -> new ServiceResponseTimeRank(
                (String) row[0],
                row[1] != null ? ((Number) row[1]).doubleValue() : 0.0,
                ((Number) row[2]).longValue()
            ))
            .toList();

        List<HourlyServiceTrend> hourlyTrend = requestLogRepository
            .findHourlyServiceTrend(start, end).stream()
            .map(row -> new HourlyServiceTrend(
                ((Number) row[0]).intValue(),
                (String) row[1],
                ((Number) row[2]).longValue()
            ))
            .toList();

        return new ServiceStatsResponse(serviceStats, slowestServices, hourlyTrend);
    }

    /**
     * 사용자별 통계 조회
     */
    @Transactional(readOnly = true)
    public UserStatsResponse getUserStats(LocalDateTime start, LocalDateTime end) {
        long totalUsers = snpUserRepository.countByIsActiveTrue();
        long totalActiveUsers = requestLogRepository.countActiveUsers(start, end);

        List<UserRequestRank> topUsers = requestLogRepository
            .findTopUsersByRequestCount(start, end).stream()
            .map(row -> new UserRequestRank(
                row[0] != null ? ((Number) row[0]).longValue() : null,
                (String) row[1],
                (String) row[2],
                ((Number) row[3]).longValue(),
                row[4] != null ? ((Number) row[4]).doubleValue() : 0.0
            ))
            .toList();

        List<UserRoleDistribution> roleDistribution = requestLogRepository
            .findUserRoleDistribution(start, end).stream()
            .map(row -> new UserRoleDistribution(
                (String) row[0],
                ((Number) row[1]).longValue(),
                ((Number) row[2]).longValue()
            ))
            .toList();

        List<DailyActiveUsers> dailyActiveUsers = requestLogRepository
            .findDailyActiveUsers(start, end).stream()
            .map(row -> new DailyActiveUsers(
                (String) row[0],
                ((Number) row[1]).longValue()
            ))
            .toList();

        long usersWithActiveKey = apiKeyRepository.countUsersWithActiveKey();

        return new UserStatsResponse(totalUsers, usersWithActiveKey, totalActiveUsers, topUsers, roleDistribution, dailyActiveUsers);
    }

    /**
     * API 엔드포인트별 통계 조회
     */
    @Transactional(readOnly = true)
    public ApiStatsResponse getApiStats(LocalDateTime start, LocalDateTime end) {
        List<ApiCallRank> topApis = requestLogRepository
            .findTopApisWithStats(start, end).stream()
            .map(row -> new ApiCallRank(
                (String) row[0],
                (String) row[1],
                (String) row[2],
                (String) row[3],
                ((Number) row[4]).longValue(),
                row[5] != null ? ((Number) row[5]).doubleValue() : 0.0,
                row[6] != null ? ((Number) row[6]).doubleValue() : 0.0
            ))
            .toList();

        List<ApiErrorRank> topErrorApis = requestLogRepository
            .findTopErrorApis(start, end).stream()
            .map(row -> new ApiErrorRank(
                (String) row[0],
                (String) row[1],
                (String) row[2],
                ((Number) row[3]).longValue(),
                ((Number) row[4]).longValue(),
                row[5] != null ? ((Number) row[5]).doubleValue() : 0.0
            ))
            .toList();

        List<ApiMethodDistribution> methodDistribution = requestLogRepository
            .findMethodDistribution(start, end).stream()
            .map(row -> new ApiMethodDistribution(
                (String) row[0],
                ((Number) row[1]).longValue()
            ))
            .toList();

        List<HttpStatusDistribution> statusCodeDistribution = requestLogRepository
            .findStatusCodeDistribution(start, end).stream()
            .map(row -> new HttpStatusDistribution(
                ((Number) row[0]).intValue(),
                ((Number) row[1]).longValue()
            ))
            .toList();

        return new ApiStatsResponse(topApis, topErrorApis, methodDistribution, statusCodeDistribution);
    }

    /**
     * 테넌트별 통계 조회
     */
    @Transactional(readOnly = true)
    public TenantStatsResponse getTenantStats(LocalDateTime start, LocalDateTime end) {
        List<TenantRequestStats> tenantStats = requestLogRepository
            .findTenantRequestStats(start, end).stream()
            .map(row -> new TenantRequestStats(
                row[0] != null ? ((Number) row[0]).longValue() : null,
                (String) row[1],
                ((Number) row[2]).longValue(),
                ((Number) row[3]).longValue(),
                row[4] != null ? ((Number) row[4]).doubleValue() : 0.0,
                row[5] != null ? ((Number) row[5]).doubleValue() : 0.0
            ))
            .toList();

        List<DailyTenantTrend> dailyTrend = requestLogRepository
            .findDailyTenantTrend(start, end).stream()
            .map(row -> new DailyTenantTrend(
                (String) row[0],
                (String) row[1],
                ((Number) row[2]).longValue()
            ))
            .toList();

        List<TenantApiKeyStats> apiKeyStats = apiKeyRepository
            .findTenantApiKeyStats().stream()
            .map(row -> new TenantApiKeyStats(
                (String) row[0],
                ((Number) row[1]).longValue(),
                ((Number) row[2]).longValue()
            ))
            .toList();

        return new TenantStatsResponse(tenantStats, dailyTrend, apiKeyStats);
    }

    /**
     * 사용량 추이 조회 (일별/주별/월별)
     */
    @Transactional(readOnly = true)
    public UsageTrendResponse getUsageTrend(String period) {
        List<Object[]> rows;
        LocalDateTime since;

        switch (period) {
            case "weekly":
                since = LocalDateTime.now().minusWeeks(12);
                rows = requestLogRepository.findWeeklyUsageTrend(since);
                break;
            case "monthly":
                since = LocalDateTime.now().minusMonths(12);
                rows = requestLogRepository.findMonthlyUsageTrend(since);
                break;
            default: // daily
                since = LocalDateTime.now().minusDays(30);
                rows = requestLogRepository.findDailyUsageTrend(since);
                period = "daily";
                break;
        }

        List<UsageTrendResponse.UsageTrendItem> items = rows.stream()
            .map(row -> {
                String label = row[0].toString();
                long total = ((Number) row[1]).longValue();
                long success = ((Number) row[2]).longValue();
                double avgRt = ((Number) row[3]).doubleValue();
                long users = ((Number) row[4]).longValue();
                double successRate = total > 0 ? (double) success / total * 100 : 0;
                return new UsageTrendResponse.UsageTrendItem(
                    label, total, success, total - success, successRate, avgRt, users
                );
            })
            .toList();

        return new UsageTrendResponse(period, items);
    }
}
