package com.makecdev.coderemotemanager;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.List;
import org.junit.Test;

public class ProBillingClientTest {
    @Test public void productQueryConnectsAndReturnsLocalizedOffer() {
        FakeStore store = new FakeStore();
        store.product = new ProBillingClient.Product("pro_multi_account", "₩9,900", "offer-token");
        ProBillingClient client = client(store, validVerifier());
        List<ProBillingClient.ProductResult> results = new ArrayList<>();
        client.getProduct(results::add);
        assertEquals(1, store.connections);
        assertEquals("₩9,900", results.get(0).localizedPrice);
        assertTrue(results.get(0).available);
    }

    @Test public void purchaseUsesOfferTokenAndVerifiesOnlyPurchasedState() {
        FakeStore store = new FakeStore();
        store.product = new ProBillingClient.Product("pro_multi_account", "$9.99", "one-time-offer");
        store.purchase = new ProBillingClient.Purchase(ProBillingClient.PURCHASED, "private-token", false);
        RecordingVerifier verifier = validVerifier();
        List<ProBillingClient.Result> results = new ArrayList<>();
        client(store, verifier).purchase(new Object(), binding(), results::add);
        assertEquals("one-time-offer", store.launchedOfferToken);
        assertEquals("private-token", verifier.token);
        assertEquals("activated", results.get(0).state);
        assertEquals("header.claims.signature", results.get(0).certificate);
    }

    @Test public void pendingAndCancellationNeverCallVerifier() {
        for (int state : new int[] { ProBillingClient.PENDING, ProBillingClient.CANCELED }) {
            FakeStore store = new FakeStore();
            store.product = product();
            store.purchase = new ProBillingClient.Purchase(state, null, false);
            RecordingVerifier verifier = validVerifier();
            List<ProBillingClient.Result> results = new ArrayList<>();
            client(store, verifier).purchase(new Object(), binding(), results::add);
            assertEquals(state == ProBillingClient.PENDING ? "pending" : "canceled", results.get(0).state);
            assertNull(verifier.token);
        }
    }

    @Test public void alreadyOwnedRestoresAndUnavailableStoreFailsSafely() {
        FakeStore owned = new FakeStore();
        owned.product = product();
        owned.launchCode = ProBillingClient.ALREADY_OWNED;
        owned.restored = new ProBillingClient.Purchase(ProBillingClient.PURCHASED, "restore-token", false);
        List<ProBillingClient.Result> restored = new ArrayList<>();
        client(owned, validVerifier()).purchase(new Object(), binding(), restored::add);
        assertEquals("activated", restored.get(0).state);
        assertEquals(1, owned.restoreQueries);

        FakeStore unavailable = new FakeStore();
        unavailable.connects = false;
        List<ProBillingClient.Result> failed = new ArrayList<>();
        client(unavailable, validVerifier()).restore(binding(), failed::add);
        assertEquals("unavailable", failed.get(0).state);
    }

    @Test public void restoreDistinguishesNotOwnedPendingAndPurchased() {
        FakeStore store = new FakeStore();
        List<ProBillingClient.Result> results = new ArrayList<>();
        ProBillingClient client = client(store, validVerifier());
        client.restore(binding(), results::add);
        assertEquals("not_owned", results.remove(0).state);
        store.restored = new ProBillingClient.Purchase(ProBillingClient.PENDING, null, false);
        client.restore(binding(), results::add);
        assertEquals("pending", results.remove(0).state);
        store.restored = new ProBillingClient.Purchase(ProBillingClient.PURCHASED, "restore-token", false);
        client.restore(binding(), results::add);
        assertEquals("activated", results.remove(0).state);
    }

    private static ProBillingClient client(FakeStore store, RecordingVerifier verifier) {
        return new ProBillingClient(store, verifier);
    }
    private static ProBillingClient.Product product() { return new ProBillingClient.Product("pro_multi_account", "$9.99", "offer"); }
    private static ProBillingClient.Binding binding() { return new ProBillingClient.Binding("a".repeat(43), "b".repeat(43)); }
    private static RecordingVerifier validVerifier() { return new RecordingVerifier(); }

    private static final class RecordingVerifier implements ProBillingClient.Verifier {
        String token;
        public void verify(String purchaseToken, ProBillingClient.Binding binding, ProBillingClient.Callback<ProBillingClient.Certificate> callback) {
            token = purchaseToken;
            callback.accept(new ProBillingClient.Certificate("header.claims.signature", "2026-09-01T00:00:00Z"));
        }
    }

    private static final class FakeStore implements ProBillingClient.Store {
        boolean connects = true;
        int connections;
        int restoreQueries;
        int launchCode = ProBillingClient.OK;
        String launchedOfferToken;
        ProBillingClient.Product product;
        ProBillingClient.Purchase purchase;
        ProBillingClient.Purchase restored;
        public void connect(ProBillingClient.Callback<Boolean> callback) { connections++; callback.accept(connects); }
        public void queryProduct(ProBillingClient.Callback<ProBillingClient.Product> callback) { callback.accept(product); }
        public int launch(Object activity, ProBillingClient.Product value, ProBillingClient.Callback<ProBillingClient.Purchase> callback) {
            launchedOfferToken = value.offerToken;
            if (launchCode == ProBillingClient.OK) callback.accept(purchase);
            return launchCode;
        }
        public void queryOwned(ProBillingClient.Callback<ProBillingClient.Purchase> callback) { restoreQueries++; callback.accept(restored); }
    }
}
