package com.gcsc.connection.monitoring.service;

import com.gcsc.connection.monitoring.dto.DashboardStatsResponse;
import com.gcsc.connection.monitoring.dto.ErrorTrendResponse;
import com.gcsc.connection.monitoring.dto.HourlyTrendResponse;
import com.gcsc.connection.monitoring.dto.RequestLogResponse;
import com.gcsc.connection.monitoring.dto.ServiceRatioResponse;
import com.gcsc.connection.monitoring.dto.TenantRatioResponse;
import com.gcsc.connection.monitoring.dto.TopApiResponse;
import com.gcsc.connection.monitoring.repository.SnpApiRequestLogRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
@RequiredArgsConstructor
public class DashboardService {

    private final SnpApiRequestLogRepository snpApiRequestLogRepository;

    /**
     * 대시보드 요약 통계 조회
     */
    @Transactional(readOnly = true)
    public DashboardStatsResponse getSummary() {
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        LocalDateTime yesterday = startOfDay.minusDays(1);

        Object[] stats = snpApiRequestLogRepository.findTodayStats(startOfDay);
        Object[] row = (Object[]) stats[0];

        long total = ((Number) row[0]).longValue();
        long successCount = ((Number) row[1]).longValue();
        double avgResponseTime = ((Number) row[2]).doubleValue();

        long yesterdayRequests = snpApiRequestLogRepository
            .countYesterdayRequests(yesterday, startOfDay);

        double changePercent = yesterdayRequests > 0
            ? ((double) (total - yesterdayRequests) / yesterdayRequests) * 100
            : 0.0;

        double successRate = total > 0
            ? ((double) successCount / total) * 100
            : 0.0;

        long failureCount = total - successCount;

        long activeUserCount = snpApiRequestLogRepository
            .countTodayActiveUsers(startOfDay);

        return new DashboardStatsResponse(
            total, yesterdayRequests, changePercent,
            successRate, failureCount, avgResponseTime, activeUserCount
        );
    }

    /**
     * 시간별 요청 추이 조회 (오늘, 0~23시)
     */
    @Transactional(readOnly = true)
    public List<HourlyTrendResponse> getHourlyTrend() {
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        List<Object[]> rows = snpApiRequestLogRepository.findHourlyTrend(startOfDay);

        Map<Integer, long[]> hourMap = new HashMap<>();
        for (Object[] row : rows) {
            int hour = ((Number) row[0]).intValue();
            String status = (String) row[1];
            long cnt = ((Number) row[2]).longValue();

            hourMap.computeIfAbsent(hour, k -> new long[2]);
            if ("SUCCESS".equals(status)) {
                hourMap.get(hour)[0] += cnt;
            } else {
                hourMap.get(hour)[1] += cnt;
            }
        }

        List<HourlyTrendResponse> result = new ArrayList<>();
        for (int h = 0; h < 24; h++) {
            long[] counts = hourMap.getOrDefault(h, new long[2]);
            result.add(new HourlyTrendResponse(h, counts[0], counts[1]));
        }
        return result;
    }

    /**
     * 서비스별 요청 비율 조회
     */
    @Transactional(readOnly = true)
    public List<ServiceRatioResponse> getServiceRatio() {
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        return snpApiRequestLogRepository.findServiceRatio(startOfDay).stream()
            .map(row -> new ServiceRatioResponse(
                (String) row[0],
                ((Number) row[1]).longValue()
            ))
            .toList();
    }

    /**
     * 에러율 추이 조회 (시간별, 서비스별)
     */
    @Transactional(readOnly = true)
    public List<ErrorTrendResponse> getErrorTrend() {
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        return snpApiRequestLogRepository.findErrorTrend(startOfDay).stream()
            .map(row -> new ErrorTrendResponse(
                ((Number) row[0]).intValue(),
                (String) row[1],
                ((Number) row[2]).doubleValue()
            ))
            .toList();
    }

    /**
     * 상위 API 랭킹 조회
     */
    @Transactional(readOnly = true)
    public List<TopApiResponse> getTopApis(int limit) {
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        return snpApiRequestLogRepository.findTopApis(startOfDay, limit).stream()
            .map(row -> new TopApiResponse(
                (String) row[0],
                (String) row[1],
                (String) row[2],
                (String) row[3],
                ((Number) row[4]).longValue()
            ))
            .toList();
    }

    /**
     * 테넌트별 요청 비율 조회
     */
    @Transactional(readOnly = true)
    public List<TenantRatioResponse> getTenantRequestRatio() {
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        return snpApiRequestLogRepository.findTenantRequestRatio(startOfDay).stream()
            .map(row -> new TenantRatioResponse(
                (String) row[0],
                ((Number) row[1]).longValue()
            ))
            .toList();
    }

    /**
     * 테넌트별 사용자 비율 조회
     */
    @Transactional(readOnly = true)
    public List<TenantRatioResponse> getTenantUserRatio() {
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        return snpApiRequestLogRepository.findTenantUserRatio(startOfDay).stream()
            .map(row -> new TenantRatioResponse(
                (String) row[0],
                ((Number) row[1]).longValue()
            ))
            .toList();
    }

    /**
     * 최근 로그 20건 조회
     */
    @Transactional(readOnly = true)
    public List<RequestLogResponse> getRecentLogs() {
        return snpApiRequestLogRepository.findTop20ByOrderByRequestedAtDesc().stream()
            .map(RequestLogResponse::from)
            .toList();
    }
}
