package ru.yandex.webmaster3.core.tracer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import lombok.Value;

/**
 * @author kravchenko99
 * @date 11/17/21
 */

@Value
public class BeautyYdbTrace {
    long readCount;
    String readQueryTime;
    String readPrepareTime;
    double readKBytes;

    long writeCount;
    String writeQueryTime;
    String writePrepareTime;

    Map<String, Map<String, String>> kbytesByTable;
    Map<String, Map<String, String>> queryTimeByTable;
    Map<String, Map<String, String>> prepareTimeByTable;

    Map<String, String> forEachByTable;

    public BeautyYdbTrace(YdbTrace trace) {
        this.readCount = trace.getReadCount().get();
        this.readQueryTime = millisToReadableString(trace.getTotalReadTimeNano());
        this.readPrepareTime = millisToReadableString(trace.getTotalReadPrepareTimeNano());
        this.readKBytes = trace.getTotalReadBytes().get() / 1024.0;

        this.writeCount = trace.getWriteCount().get();
        this.writeQueryTime = millisToReadableString(trace.getTotalWriteTimeNano());
        this.writePrepareTime = millisToReadableString(trace.getTotalWritePrepareTimeNano());

        this.kbytesByTable = bytesToKBytes(trace.getBytesByTable());
        this.queryTimeByTable = convertMap(trace.getTimeNanoByTable());
        this.prepareTimeByTable = convertMap(trace.getPrepareTimeNanoByTable());

        this.forEachByTable = convertMillisToDuration(trace.getStreamTimeByTable());
    }



    private static Map<String, Map<String, String>> bytesToKBytes(Map<String, Map<String, AtomicLong>> rawMap) {
        Map<String, Map<String, String>> result = new HashMap<>(rawMap.size());

        for (var entry : rawMap.entrySet()) {
            Map<String, String> timeByOp = new HashMap<>(entry.getValue().size());
            result.put(entry.getKey(), timeByOp);
            for (var e: entry.getValue().entrySet()) {
                timeByOp.put(e.getKey(), String.format("%.5f", e.getValue().get() / 1024.0));
            }
        }

        return result;
    }

    private static String millisToReadableString(AtomicLong totalReadTimeMillis) {
        return Duration.ofMillis(totalReadTimeMillis.get()).toString();
    }

    private static Map<String, String> convertMillisToDuration(Map<String, AtomicLong> rawStats) {
        Map<String, String> result = new HashMap<>(rawStats.size());
        rawStats.forEach((tableName, nanos) -> result.put(tableName, millisToReadableString(nanos)));
        return result;
    }

    private static Map<String, Map<String, String>> convertMap(Map<String, Map<String, AtomicLong>> rawStats) {
        Map<String, Map<String, String>> result = new HashMap<>(rawStats.size());
        for (var entry : rawStats.entrySet()) {
            result.put(entry.getKey(), convertMillisToDuration(entry.getValue()));
        }
        return result;
    }
}
