package peergos.shared.user;

import jsinterop.annotations.*;
import peergos.shared.*;
import peergos.shared.cbor.*;
import peergos.shared.crypto.hash.*;
import peergos.shared.crypto.symmetric.*;
import peergos.shared.user.fs.*;
import peergos.shared.util.*;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;

@JsType
public class EntryPoint implements Cborable {

    public final Capability pointer;
    public final String ownerName;
    public final PublicKeyHash owner;
    public final Set<String> readers, writers;

    public EntryPoint(Capability pointer, String ownerName, PublicKeyHash owner, Set<String> readers, Set<String> writers) {
        if (! pointer.writer.isPresent())
            throw new IllegalStateException("EntryPoint requires a non relative capability!");
        this.pointer = pointer;
        this.ownerName = ownerName;
        this.owner = owner;
        this.readers = readers;
        this.writers = writers;
    }

    public byte[] serializeAndSymmetricallyEncrypt(SymmetricKey key) {
        byte[] nonce = key.createNonce();
        return ArrayOps.concat(nonce, key.encrypt(serialize(), nonce));
    }

    /**
     *
     * @param path The path of the file this entry point corresponds to
     * @param network
     * @return
     */
    public CompletableFuture<Boolean> isValid(String path, NetworkAccess network) {
        String[] parts = path.split("/");
        String claimedOwner = parts[1];
        // check claimed owner actually owns the signing key
        PublicKeyHash entryWriter = pointer.writer.get();
        return network.coreNode.getPublicKeyHash(claimedOwner).thenCompose(ownerKey -> {
            if (! ownerKey.isPresent())
                throw new IllegalStateException("No owner key present for user " + claimedOwner);
           return UserContext.getWriterData(network, ownerKey.get(), ownerKey.get()).thenApply(wd -> {
               // TODO do this recursively to handle arbitrary trees of key ownership
               return wd.props.ownedKeys.contains(entryWriter);
           });
        });
    }

    @Override
    @SuppressWarnings("unusable-by-js")
    public CborObject toCbor() {
        Map<String, CborObject> cbor = new TreeMap<>();
        cbor.put("c", pointer.toCbor());
        cbor.put("n", new CborObject.CborString(ownerName));
        cbor.put("o", owner.toCbor());
        cbor.put("r", new CborObject.CborList(readers.stream().sorted().map(CborObject.CborString::new).collect(Collectors.toList())));
        cbor.put("w", new CborObject.CborList(writers.stream().sorted().map(CborObject.CborString::new).collect(Collectors.toList())));
        return CborObject.CborMap.build(cbor);
    }

    public static EntryPoint fromCbor(Cborable cbor) {
        if (! (cbor instanceof CborObject.CborMap))
            throw new IllegalStateException("Incorrect cbor type for EntryPoint: " + cbor);

        SortedMap<CborObject, ? extends Cborable> map = ((CborObject.CborMap) cbor).values;
        Capability pointer = Capability.fromCbor(map.get(new CborObject.CborString("c")));
        String ownerName = ((CborObject.CborString) map.get(new CborObject.CborString("n"))).value;
        PublicKeyHash owner = PublicKeyHash.fromCbor(map.get(new CborObject.CborString("o")));
        Set<String> readers = ((CborObject.CborList) map.get(new CborObject.CborString("r"))).value
                .stream()
                .map(c -> ((CborObject.CborString) c).value)
                .collect(Collectors.toSet());
        Set<String> writers = ((CborObject.CborList) map.get(new CborObject.CborString("w"))).value
                .stream()
                .map(c -> ((CborObject.CborString) c).value)
                .collect(Collectors.toSet());
        return new EntryPoint(pointer, ownerName, owner, readers, writers);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        EntryPoint that = (EntryPoint) o;

        if (pointer != null ? !pointer.equals(that.pointer) : that.pointer != null) return false;
        if (ownerName != null ? !ownerName.equals(that.ownerName) : that.ownerName != null) return false;
        if (readers != null ? !readers.equals(that.readers) : that.readers != null) return false;
        return writers != null ? writers.equals(that.writers) : that.writers == null;

    }

    @Override
    public int hashCode() {
        int result = pointer != null ? pointer.hashCode() : 0;
        result = 31 * result + (ownerName != null ? ownerName.hashCode() : 0);
        result = 31 * result + (readers != null ? readers.hashCode() : 0);
        result = 31 * result + (writers != null ? writers.hashCode() : 0);
        return result;
    }

    static EntryPoint symmetricallyDecryptAndDeserialize(byte[] input, SymmetricKey key) throws IOException {
        byte[] nonce = Arrays.copyOfRange(input, 0, 24);
        byte[] raw = key.decrypt(Arrays.copyOfRange(input, 24, input.length), nonce);
        return fromCbor(CborObject.fromByteArray(raw));
    }
}

@@ -4007,17 +4007,16 @@
  false;%0A
-%0A
         
@@ -4049,25 +4049,24 @@
 int) o;%0A
-%0A
         
 if (poin
@@ -4061,46 +4061,30 @@
     
-if (pointer != null ? !pointer
+return Objects
 .equals(
 that
@@ -4083,23 +4083,16 @@
 als(
-that.
 pointer
-) :
+,
  tha
@@ -4104,82 +4104,44 @@
 nter
- != null) return false;%0A        if (ownerName != null ? !ownerName
+) &&%0A                Objects
 .equals(
 that
@@ -4136,21 +4136,16 @@
 .equals(
-that.
 ownerNam
@@ -4145,19 +4145,17 @@
 wnerName
-) :
+,
  that.ow
@@ -4165,69 +4165,88 @@
 Name
- != null) return false;%0A        if (readers != null ? !reader
+) &&%0A                Objects.equals(owner, that.owner) &&%0A                Object
 s.eq
@@ -4254,23 +4254,16 @@
 als(
-that.
 readers
-) :
+,
  tha
@@ -4275,71 +4275,35 @@
 ders
- != null) return false;%0A        return writers != null ? writer
+) &&%0A                Object
 s.eq
@@ -4311,23 +4311,16 @@
 als(
-that.
 writers
-) :
+,
  tha
@@ -4332,18 +4332,10 @@
 ters
- == null;%0A
+);
 %0A   
@@ -4392,313 +4392,72 @@
     
-int result = pointer != null ? pointer.hashCode() : 0;%0A        result = 31 * result + (ownerName != null ? ownerName.hashCode() : 0);%0A        result = 31 * result + (readers != null ? readers.hashCode() : 0);%0A        result = 31 * result + (writers != null ? writers.hashCode() : 0);%0A        return result
+return Objects.hash(pointer, ownerName, owner, readers, writers)
 ;%0A  
@@ -4553,27 +4553,8 @@
 key)
- throws IOException
  %7B%0A 

Fix equals method