package com.makecdev.coderemotemanager;

public final class ProBillingClient {
    public static final int OK = 0;
    public static final int CANCELED = 1;
    public static final int ALREADY_OWNED = 2;
    public static final int PURCHASED = 3;
    public static final int PENDING = 4;

    public interface Callback<T> { void accept(T value); }
    public interface Store {
        void connect(Callback<Boolean> callback);
        void queryProduct(Callback<Product> callback);
        int launch(Object activity, Product product, Callback<Purchase> callback);
        void queryOwned(Callback<Purchase> callback);
    }
    public interface Verifier {
        void verify(String purchaseToken, Binding binding, Callback<Certificate> callback);
    }

    public static final class Product {
        public final String productId, localizedPrice, offerToken;
        public Product(String productId, String localizedPrice, String offerToken) {
            this.productId = productId; this.localizedPrice = localizedPrice; this.offerToken = offerToken;
        }
    }
    public static final class Purchase {
        public final int state; public final String token; public final boolean acknowledged;
        public Purchase(int state, String token, boolean acknowledged) { this.state = state; this.token = token; this.acknowledged = acknowledged; }
    }
    public static final class Binding {
        public final String backendKeyThumbprint, challengeHash;
        public Binding(String backendKeyThumbprint, String challengeHash) {
            this.backendKeyThumbprint = backendKeyThumbprint; this.challengeHash = challengeHash;
        }
    }
    public static final class Certificate {
        public final String certificate, expiresAt;
        public Certificate(String certificate, String expiresAt) { this.certificate = certificate; this.expiresAt = expiresAt; }
    }
    public static final class ProductResult {
        public final String productId, localizedPrice; public final boolean available;
        private ProductResult(String productId, String localizedPrice, boolean available) {
            this.productId = productId; this.localizedPrice = localizedPrice; this.available = available;
        }
    }
    public static final class Result {
        public final String state, certificate, expiresAt;
        private Result(String state, String certificate, String expiresAt) {
            this.state = state; this.certificate = certificate; this.expiresAt = expiresAt;
        }
    }

    private final Store store;
    private final Verifier verifier;
    public ProBillingClient(Store store, Verifier verifier) { this.store = store; this.verifier = verifier; }

    public void getProduct(Callback<ProductResult> callback) {
        store.connect(connected -> {
            if (!connected) { callback.accept(new ProductResult("pro_multi_account", null, false)); return; }
            store.queryProduct(product -> callback.accept(product == null
                ? new ProductResult("pro_multi_account", null, false)
                : new ProductResult(product.productId, product.localizedPrice, true)));
        });
    }

    public void purchase(Object activity, Binding binding, Callback<Result> callback) {
        store.connect(connected -> {
            if (!connected) { callback.accept(result("unavailable")); return; }
            store.queryProduct(product -> {
                if (product == null) { callback.accept(result("unavailable")); return; }
                int code = store.launch(activity, product, purchase -> handlePurchase(purchase, binding, callback, false));
                if (code == ALREADY_OWNED) restore(binding, callback);
                else if (code == CANCELED) callback.accept(result("canceled"));
                else if (code != OK) callback.accept(result("unavailable"));
            });
        });
    }

    public void restore(Binding binding, Callback<Result> callback) {
        store.connect(connected -> {
            if (!connected) { callback.accept(result("unavailable")); return; }
            store.queryOwned(purchase -> handlePurchase(purchase, binding, callback, true));
        });
    }

    private void handlePurchase(Purchase purchase, Binding binding, Callback<Result> callback, boolean restore) {
        if (purchase == null) { callback.accept(result(restore ? "not_owned" : "unavailable")); return; }
        if (purchase.state == PENDING) { callback.accept(result("pending")); return; }
        if (purchase.state == CANCELED) { callback.accept(result("canceled")); return; }
        if (purchase.state != PURCHASED || purchase.token == null) { callback.accept(result("unavailable")); return; }
        verifier.verify(purchase.token, binding, certificate -> callback.accept(certificate == null
            ? result("unavailable")
            : new Result("activated", certificate.certificate, certificate.expiresAt)));
    }

    private static Result result(String state) { return new Result(state, null, null); }
}
