package ru.yandex.qe.dispenser.ws.admin;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.cxf.jaxrs.client.WebClient;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.qe.dispenser.api.v1.DiAmount;
import ru.yandex.qe.dispenser.api.v1.DiPerformer;
import ru.yandex.qe.dispenser.api.v1.DiUnit;
import ru.yandex.qe.dispenser.api.v1.request.DiEntity;
import ru.yandex.qe.dispenser.api.v1.request.DiEntityUsage;
import ru.yandex.qe.dispenser.client.v1.DiOAuthToken;
import ru.yandex.qe.dispenser.client.v1.Dispenser;
import ru.yandex.qe.dispenser.client.v1.builder.BatchQuotaChangeRequestBuilder;
import ru.yandex.qe.dispenser.client.v1.impl.DispenserConfig;
import ru.yandex.qe.dispenser.client.v1.impl.RemoteDispenserFactory;

public enum EntityUploader2 {
    ;

    private static final Logger LOG = LoggerFactory.getLogger(EntityUploader.class);

    private static final String[] RESOURCES = {"yt-disk", "yt-node", "yt-chunk"};
    private static final String PROJECT = "default";
    private static final DiPerformer PERFORMER = DiPerformer.login("amosov-f").chooses(PROJECT);

    private static final int BATCH_SIZE = 10000;
    private static final int MAX_PARSED_ENTITIES = 10000;
    private static final int MAX_CONCURRENT_REQUESTS = 10;

    private static final int RECIEVE_TIMEOUT = 100000;

    private static final int PRODUCTION_ENTITY_COUNT = 15_000_000;

    @SuppressWarnings("OverlyLongMethod")
    public static void main(@NotNull final String[] args) throws IOException, InterruptedException, ParseException {
        final long start = System.currentTimeMillis();

        final Options options = new Options();
        options.addOption(new Option("h", "host", true, "target host"));
        options.addOption(new Option("t", "token", true, "zombie oauth token"));
        options.addOption(new Option("u", "url", true, "table url"));
        final CommandLineParser clp = new PosixParser();
        final CommandLine cl = clp.parse(options, args);

        final String host = cl.getOptionValue('h');
        final String oauthToken = cl.getOptionValue('t');
        final String url = cl.getOptionValue('u');

        final DispenserConfig config = new DispenserConfig();
        config.setDispenserHost(host);
        config.setServiceZombieOAuthToken(DiOAuthToken.of(oauthToken));
        config.setReceiveTimeout(RECIEVE_TIMEOUT);
        final Dispenser dispenser = new RemoteDispenserFactory(config).get();

        final Response response = WebClient.create(url).acceptEncoding("gzip").get();
        InputStream in = (InputStream) response.getEntity();
        if (Optional.ofNullable(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).orElse("").contains("gzip")) {
            in = new GZIPInputStream(in);
        }
        final AtomicInteger totalCounter = new AtomicInteger();
        final AtomicInteger counter = new AtomicInteger();
        AtomicReference<BatchQuotaChangeRequestBuilder> ref = new AtomicReference<>(dispenser.quotas().changeInService("nirvana"));
        try (BufferedReader reader = new BufferedReader(new FileReader("/Users/vkokarev/Downloads/result.txt"))) {
            reader.lines().forEach(line -> {
                final BatchQuotaChangeRequestBuilder builder = ref.get();

                final EntityInfo entityInfo = parse(line);

                builder.createEntity(entityInfo.getEntity(), DiPerformer.login(entityInfo.getOwner()).chooses("default"));
                totalCounter.incrementAndGet();
                counter.incrementAndGet();
                entityInfo.getUsages().entrySet().forEach(entry -> {
                    counter.incrementAndGet();
                    final String login = entry.getKey();
                    final Long count = entry.getValue();
                    builder.shareEntity(DiEntityUsage.of(entityInfo.getEntity(), count.intValue()), DiPerformer.login(login).chooses("default"));
                });

                if (counter.get() > BATCH_SIZE) {
                    builder.perform();
                    ref.set(dispenser.quotas().changeInService("nirvana"));
                    counter.set(0);
                    System.out.println(totalCounter + " lines");
                    System.out.println("totl time: " + ((System.currentTimeMillis() - start) / 60000.) + " min");
                }
            });
            ref.get().perform();
        }
        System.out.println("totl time: " + ((System.currentTimeMillis() - start) / 60000.) + " min");
    }


    private static EntityInfo parse(@NotNull final String line) {
        final String[] parts = line.split("\t");
        //uid \t path \t disk_space \t chunk_count \t node_count \t usage_count \t used_by
        final String key = parts[0];
        final long disk = Long.parseLong(parts[2]);
        final long chunks = Long.parseLong(parts[3]);
        final long nodes = Long.parseLong(parts[4]);

        final String[] users = parts[6].split(",");
        final String owner = users[0];
        final Map<String, Long> usages = new HashMap<>();
        for (int i = 1; i < users.length; i++) {
            final String user = users[i];
            if (!"robot-yeti-v-nir".equals(user)) {
                usages.putIfAbsent(user, 0L);
                usages.put(user, usages.get(user) + 1);
            }
        }

        final DiEntity entity = DiEntity.withKey(key)
                .bySpecification("yt-file")
                .occupies("yt-disk", DiAmount.of(disk, DiUnit.BYTE))
                .occupies("yt-chunk", DiAmount.of(chunks, DiUnit.COUNT))
                .occupies("yt-node", DiAmount.of(nodes, DiUnit.COUNT))
                .build();

        return new EntityInfo(entity, owner, usages);
    }


    private static class EntityInfo {
        private final DiEntity entity;
        private final String owner;
        private final Map<String, Long> usages;

        private EntityInfo(final DiEntity entity, final String owner, final Map<String, Long> usages) {
            this.entity = entity;
            this.owner = owner;
            this.usages = usages;
        }

        public String getOwner() {
            return owner;
        }

        public Map<String, Long> getUsages() {
            return usages;
        }

        public DiEntity getEntity() {
            return entity;
        }
    }
}