package peergos.server;

import peergos.shared.*;
import peergos.shared.corenode.*;
import peergos.shared.crypto.hash.*;
import peergos.shared.io.ipfs.multihash.*;
import peergos.shared.user.*;

import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.time.*;
import java.util.*;
import java.util.stream.*;

public class UserStats {

    public static void main(String[] args) throws Exception {
        Crypto crypto = Crypto.initJava();
        NetworkAccess network = NetworkAccess.buildJava(new URL("https://alpha.peergos.net")).get();
        List<String> usernames = network.coreNode.getUsernames("").get();
        List<Summary> summaries = usernames.stream().parallel().flatMap(username -> {
            try {
                List<UserPublicKeyLink> chain = network.coreNode.getChain(username).get();
                UserPublicKeyLink last = chain.get(chain.size() - 1);
                LocalDate expiry = last.claim.expiry;
                List<Multihash> hosts = last.claim.storageProviders;
                PublicKeyHash owner = last.owner;
                Set<PublicKeyHash> ownedKeysRecursive =
                        WriterData.getOwnedKeysRecursive(username, network.coreNode, network.mutable, network.dhtClient).join();
                long total = 0;
                for (PublicKeyHash writer : ownedKeysRecursive) {
                    MaybeMultihash target = network.mutable.getPointerTarget(owner, writer, network.dhtClient).get();
                    if (target.isPresent())
                        total += network.dhtClient.getRecursiveBlockSize(target.get()).get();
                }
                String summary = "User: " + username + ", expiry: " + expiry + " usage: " + total + "\n";
                System.out.println(summary);
                return Stream.of(new Summary(username, expiry, total, hosts));
            } catch (Exception e) {
                System.err.println("Error for " + username);
                e.printStackTrace();
                return Stream.empty();
            }
        }).collect(Collectors.toList());

        // Sort by usage
        sortAndPrint(summaries, (a, b) -> (int) (b.usage - a.usage), "usage.txt");

        // Sort by expiry
        sortAndPrint(summaries, (a, b) -> a.expiry.compareTo(b.expiry), "expiry.txt");

        // Sort by host
        sortAndPrint(summaries, Comparator.comparing(s -> s.storageProviders.stream()
                .findFirst()
                .map(Object::toString)
                .orElse("")), "host.txt");
    }

    private static void sortAndPrint(List<Summary> stats,
                                     Comparator<Summary> order,
                                     String filename) throws Exception {
        stats.sort(order);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        stats.stream()
                .map(s -> (s.toString() + "\n").getBytes())
                .forEach(bytes -> bout.write(bytes, 0, bytes.length));
        Files.write(Paths.get(filename), bout.toByteArray());
    }

    private static class Summary {
        public final String username;
        public final LocalDate expiry;
        public final long usage;
        public final List<Multihash> storageProviders;

        public Summary(String username, LocalDate expiry, long usage, List<Multihash> storageProviders) {
            this.username = username;
            this.expiry = expiry;
            this.usage = usage;
            this.storageProviders = storageProviders;
        }

        public String toString() {
            return "User: " + username + ", expiry: " + expiry + ", usage: " + usage + ", hosts: " + storageProviders;
        }
    }
}

@@ -1715,16 +1715,87 @@
  + total
+%0A                        + %22, owned keys: %22 + ownedKeysRecursive.size()
  + %22%5Cn%22;
@@ -1915,16 +1915,36 @@
 l, hosts
+, ownedKeysRecursive
 ));%0A    
@@ -3329,16 +3329,67 @@
 oviders;
+%0A        public final Set%3CPublicKeyHash%3E ownedKeys;
 %0A%0A      
@@ -3484,16 +3484,46 @@
 roviders
+, Set%3CPublicKeyHash%3E ownedKeys
 ) %7B%0A    
@@ -3668,32 +3668,72 @@
 orageProviders;%0A
+            this.ownedKeys = ownedKeys;%0A
         %7D%0A%0A     
@@ -3846,16 +3846,36 @@
  + usage
+%0A                   
  + %22, ho
@@ -3891,32 +3891,70 @@
 storageProviders
+ + %22, owned keys: %22 + ownedKeys.size()
 ;%0A        %7D%0A    

Add number of owned keys to stats