package io.synor.wallet;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * Synor Wallet SDK for Java.
 *
 * Manage wallets, sign transactions, and query balances on the Synor blockchain.
 *
 * Example:
 * <pre>{@code
 * SynorWallet wallet = new SynorWallet("your-api-key");
 * CreateWalletResult result = wallet.createWallet(WalletType.STANDARD).join();
 * System.out.println("Address: " + result.getWallet().getAddress());
 * }</pre>
 */
public class SynorWallet {
    private static final String DEFAULT_ENDPOINT = "https://wallet.synor.cc/api/v1";
    private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(30);

    private final WalletConfig config;
    private final HttpClient httpClient;
    private final Gson gson;

    public SynorWallet(String apiKey) {
        this(WalletConfig.builder().apiKey(apiKey).build());
    }

    public SynorWallet(WalletConfig config) {
        this.config = config;
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(config.getTimeoutSeconds()))
            .build();
        this.gson = new GsonBuilder()
            .setFieldNamingPolicy(com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .create();
    }

    /**
     * Create a new wallet.
     */
    public CompletableFuture<CreateWalletResult> createWallet(WalletType type) {
        var body = new CreateWalletRequest(type, config.getNetwork());
        return post("/wallets", body, CreateWalletResult.class);
    }

    /**
     * Import a wallet from mnemonic.
     */
    public CompletableFuture<Wallet> importWallet(String mnemonic, String passphrase, WalletType type) {
        var body = new ImportWalletRequest(mnemonic, passphrase, type, config.getNetwork());
        return post("/wallets/import", body, WalletResponse.class)
            .thenApply(WalletResponse::getWallet);
    }

    /**
     * Get a wallet by ID.
     */
    public CompletableFuture<Wallet> getWallet(String walletId) {
        return get("/wallets/" + walletId, WalletResponse.class)
            .thenApply(WalletResponse::getWallet);
    }

    /**
     * Sign a transaction.
     */
    public CompletableFuture<SignedTransaction> signTransaction(String walletId, Transaction tx) {
        var body = new SignTransactionRequest(tx);
        return post("/wallets/" + walletId + "/sign", body, SignedTransactionResponse.class)
            .thenApply(SignedTransactionResponse::getSignedTransaction);
    }

    /**
     * Sign a message.
     */
    public CompletableFuture<SignedMessage> signMessage(String walletId, String message, String format) {
        var body = new SignMessageRequest(message, format != null ? format : "text");
        return post("/wallets/" + walletId + "/sign-message", body, SignedMessage.class);
    }

    /**
     * Get balance for an address.
     */
    public CompletableFuture<BalanceResponse> getBalance(String address, boolean includeTokens) {
        String path = "/balances/" + address + "?includeTokens=" + includeTokens;
        return get(path, BalanceResponse.class);
    }

    /**
     * Get UTXOs for an address.
     */
    public CompletableFuture<UTXO[]> getUTXOs(String address) {
        return get("/utxos/" + address, UTXOsResponse.class)
            .thenApply(UTXOsResponse::getUtxos);
    }

    /**
     * Estimate transaction fee.
     */
    public CompletableFuture<FeeEstimate> estimateFee(Priority priority) {
        String path = "/fees/estimate?priority=" + priority.getValue();
        return get(path, FeeEstimate.class);
    }

    private <T> CompletableFuture<T> get(String path, Class<T> responseType) {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(config.getEndpoint() + path))
            .header("Authorization", "Bearer " + config.getApiKey())
            .header("Content-Type", "application/json")
            .header("X-Network", config.getNetwork().getValue())
            .timeout(Duration.ofSeconds(config.getTimeoutSeconds()))
            .GET()
            .build();

        return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(response -> handleResponse(response, responseType));
    }

    private <T> CompletableFuture<T> post(String path, Object body, Class<T> responseType) {
        String jsonBody = gson.toJson(body);

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(config.getEndpoint() + path))
            .header("Authorization", "Bearer " + config.getApiKey())
            .header("Content-Type", "application/json")
            .header("X-Network", config.getNetwork().getValue())
            .timeout(Duration.ofSeconds(config.getTimeoutSeconds()))
            .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
            .build();

        return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(response -> handleResponse(response, responseType));
    }

    private <T> T handleResponse(HttpResponse<String> response, Class<T> responseType) {
        if (response.statusCode() >= 400) {
            ErrorResponse error = gson.fromJson(response.body(), ErrorResponse.class);
            throw new WalletException(
                error != null ? error.getMessage() : "Unknown error",
                response.statusCode(),
                error != null ? error.getCode() : null
            );
        }
        return gson.fromJson(response.body(), responseType);
    }

    // Request/Response DTOs
    private record CreateWalletRequest(WalletType type, Network network) {}
    private record ImportWalletRequest(String mnemonic, String passphrase, WalletType type, Network network) {}
    private record SignTransactionRequest(Transaction transaction) {}
    private record SignMessageRequest(String message, String format) {}
    private record WalletResponse(Wallet wallet) { Wallet getWallet() { return wallet; } }
    private record SignedTransactionResponse(SignedTransaction signedTransaction) { SignedTransaction getSignedTransaction() { return signedTransaction; } }
    private record UTXOsResponse(UTXO[] utxos) { UTXO[] getUtxos() { return utxos; } }
    private record ErrorResponse(String message, String code) { String getMessage() { return message; } String getCode() { return code; } }
}
