package nl.martijndwars.webpush;

import java.time.Duration;
import org.bouncycastle.jce.interfaces.ECPublicKey;

import java.net.MalformedURLException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Notification {
    /**
     * The endpoint associated with the push subscription
     */
    private final String endpoint;

    /**
     * The client's public key
     */
    private final ECPublicKey userPublicKey;

    /**
     * The client's auth
     */
    private final byte[] userAuth;

    /**
     * An arbitrary payload
     */
    private final byte[] payload;

    /**
     * Time in seconds that the push message is retained by the push service
     */
    private final int ttl;

    private static Duration TTL = Duration.ofDays(28);


    public Notification(String endpoint, ECPublicKey userPublicKey, byte[] userAuth, byte[] payload, int ttl) {
        this.endpoint = endpoint;
        this.userPublicKey = userPublicKey;
        this.userAuth = userAuth;
        this.payload = payload;
        this.ttl = ttl;
    }

    public Notification(String endpoint, PublicKey userPublicKey, byte[] userAuth, byte[] payload, int ttl) {
        this(endpoint, (ECPublicKey) userPublicKey, userAuth, payload, ttl);
    }

    public Notification(String endpoint, String userPublicKey, String userAuth, byte[] payload, int ttl)  throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        this(endpoint, Utils.loadPublicKey(userPublicKey), Base64Encoder.decode(userAuth), payload, ttl);
    }

    public Notification(String endpoint, PublicKey userPublicKey, byte[] userAuth, byte[] payload) {
        this(endpoint, userPublicKey, userAuth, payload, (int) TTL.getSeconds());
    }

    public Notification(String endpoint, String userPublicKey, String userAuth, byte[] payload) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        this(endpoint, Utils.loadPublicKey(userPublicKey), Base64Encoder.decode(userAuth), payload);
    }

    public Notification(String endpoint, String userPublicKey, String userAuth, String payload) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        this(endpoint, Utils.loadPublicKey(userPublicKey), Base64Encoder.decode(userAuth), payload.getBytes(UTF_8));
    }

    public Notification(Subscription subscription, String payload) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        this(subscription.endpoint, subscription.keys.p256dh, subscription.keys.auth, payload);
    }

    public String getEndpoint() {
        return endpoint;
    }

    public ECPublicKey getUserPublicKey() {
        return userPublicKey;
    }

    public byte[] getUserAuth() {
        return userAuth;
    }

    public byte[] getPayload() {
        return payload;
    }

    public boolean hasPayload() {
        return getPayload().length > 0;
    }

    /**
     * Detect if the notification is for a GCM-based subscription
     *
     * @return
     */
    public boolean isGcm() {
        return getEndpoint().indexOf("https://android.googleapis.com/gcm/send") == 0;
    }

    public boolean isFcm() {
        return getEndpoint().indexOf("https://fcm.googleapis.com/fcm/send") == 0;
    }

    public int getTTL() {
        return ttl;
    }

    public String getOrigin() throws MalformedURLException {
        URL url = new URL(getEndpoint());

        return url.getProtocol() + "://" + url.getHost();
    }

    public static NotificationBuilder builder() {
        return new Notification.NotificationBuilder();
    }

    public static class NotificationBuilder {
        private String endpoint = null;
        private String userPublicKey = null;
        private String userAuth = null;
        private byte[] payload = null;
        private int ttl = (int) TTL.getSeconds();

        private NotificationBuilder() {
        }

        public Notification build() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
            return new Notification(endpoint, userPublicKey, userAuth, payload, ttl);
        }

        public NotificationBuilder endpoint(String endpoint) {
            this.endpoint = endpoint;
            return this;
        }
        public NotificationBuilder userPublicKey(String publicKey) {
            this.userPublicKey = publicKey;
            return this;
        }
        public NotificationBuilder userAuth(String userAuth) {
            this.userAuth = userAuth;
            return this;
        }
        public NotificationBuilder payload(byte[] payload) {
            this.payload = payload;
            return this;
        }
        public NotificationBuilder ttl(int ttl) {
            this.ttl = ttl;
            return this;
        }
    }
}

@@ -3919,30 +3919,35 @@
     private 
-String
+ECPublicKey
  userPublicK
@@ -3973,22 +3973,22 @@
 private 
-String
+byte%5B%5D
  userAut
@@ -4177,90 +4177,8 @@
 ld()
- throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException
  %7B%0A 
@@ -4413,74 +4413,323 @@
   %7D%0A
-        public NotificationBuilder userPublicKey(String publicKey)
+%0A        public NotificationBuilder userPublicKey(PublicKey publicKey) %7B%0A            this.userPublicKey = (ECPublicKey) publicKey;%0A            return this;%0A        %7D%0A%0A        public NotificationBuilder userPublicKey(String publicKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException
  %7B%0A 
@@ -4760,16 +4760,50 @@
 icKey = 
+(ECPublicKey) Utils.loadPublicKey(
 publicKe
@@ -4795,32 +4795,33 @@
 licKey(publicKey
+)
 ;%0A            re
@@ -4833,32 +4833,33 @@
 this;%0A        %7D%0A
+%0A
         public N
@@ -4901,24 +4901,183 @@
 userAuth) %7B%0A
+            this.userAuth = Base64Encoder.decode(userAuth);%0A            return this;%0A        %7D%0A%0A        public NotificationBuilder userAuth(byte%5B%5D userAuth) %7B%0A
             
@@ -5129,32 +5129,33 @@
 this;%0A        %7D%0A
+%0A
         public N
@@ -5266,24 +5266,25 @@
 ;%0A        %7D%0A
+%0A
         publ
@@ -5390,10 +5390,11 @@
 %7D%0A    %7D%0A
+%0A
 %7D%0A

add overloads for publicKey and auth setters of builder