package ru.soune.nocopy.service.statistic;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.soune.nocopy.dto.statistic.ProtectedFilesStatisticResponse;
import ru.soune.nocopy.repository.FileEntityRepository;
import ru.soune.nocopy.util.PercentileCalculator;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class ProtectedFilesStatisticService {
    private static final int PERCENTILE_5 = 5;

    private static final int PERCENTILE_50 = 50;

    private static final int PERCENTILE_95 = 95;

    private static final double BYTES_TO_MB = 1048576.0;

    private static final double BYTES_TO_GB = 1073741824.0;

    private final FileEntityRepository fileEntityRepository;

    public ProtectedFilesStatisticResponse getProtectedFilesStatistics() {
        List<Object[]> totalStats = fileEntityRepository.getProtectedFilesTotalStats();
        Long totalFiles = extractTotalFiles(totalStats);
        Long totalSize = extractTotalSize(totalStats);

        List<Object[]> userStats = fileEntityRepository.getProtectedFilesPerUserStats();

        List<Long> fileCounts = extractFileCounts(userStats);
        List<Long> fileSizes = extractFileSizes(userStats);

        PercentileCalculator.PercentilesResult filesPercentiles =
                PercentileCalculator.calculatePercentiles(fileCounts, PERCENTILE_5, PERCENTILE_50, PERCENTILE_95);
        PercentileCalculator.PercentilesResult sizePercentiles =
                PercentileCalculator.calculatePercentiles(fileSizes, PERCENTILE_5, PERCENTILE_50, PERCENTILE_95);

        return buildResponse(totalFiles, totalSize, filesPercentiles, sizePercentiles);
    }

    private Long extractTotalFiles(List<Object[]> totalStats) {
        if (totalStats.isEmpty() || totalStats.get(0) == null) {
            return 0L;
        }
        Object[] row = totalStats.get(0);
        return row[0] != null ? ((Number) row[0]).longValue() : 0L;
    }

    private Long extractTotalSize(List<Object[]> totalStats) {
        if (totalStats.isEmpty() || totalStats.get(0) == null) {
            return 0L;
        }
        Object[] row = totalStats.get(0);
        return row[1] != null ? ((Number) row[1]).longValue() : 0L;
    }

    private List<Long> extractFileCounts(List<Object[]> userStats) {
        return userStats.stream()
                .map(row -> row[1] != null ? ((Number) row[1]).longValue() : 0L)
                .collect(Collectors.toList());
    }

    private List<Long> extractFileSizes(List<Object[]> userStats) {
        return userStats.stream()
                .map(row -> row[2] != null ? ((Number) row[2]).longValue() : 0L)
                .collect(Collectors.toList());
    }

    private ProtectedFilesStatisticResponse buildResponse(
            Long totalFiles,
            Long totalSize,
            PercentileCalculator.PercentilesResult filesPercentiles,
            PercentileCalculator.PercentilesResult sizePercentiles) {

        ProtectedFilesStatisticResponse.Percentiles filesPerUser =
                new ProtectedFilesStatisticResponse.Percentiles(
                        filesPercentiles.getP5(),
                        filesPercentiles.getP50(),
                        filesPercentiles.getP95());

        ProtectedFilesStatisticResponse.SizePercentiles sizePerUser =
                new ProtectedFilesStatisticResponse.SizePercentiles(
                        sizePercentiles.getP5(),
                        sizePercentiles.getP50(),
                        sizePercentiles.getP95(),
                        convertBytesToMb(sizePercentiles.getP5()),
                        convertBytesToMb(sizePercentiles.getP50()),
                        convertBytesToMb(sizePercentiles.getP95()));

        return ProtectedFilesStatisticResponse.builder()
                .totalFiles(totalFiles)
                .totalSizeBytes(totalSize)
                .totalSizeGb(convertBytesToGb(totalSize))
                .filesPerUser(filesPerUser)
                .sizePerUser(sizePerUser)
                .build();
    }

    private Double convertBytesToMb(Long bytes) {
        if (bytes == null || bytes == 0) {
            return 0.0;
        }
        return bytes / BYTES_TO_MB;
    }

    private Double convertBytesToGb(Long bytes) {
        if (bytes == null || bytes == 0) {
            return 0.0;
        }
        return bytes / BYTES_TO_GB;
    }
}