package com.r35157.jupiterperpsalarm.impl.ref;

import com.r35157.libs.jupiter.perps.JupiterPerpsPosition;
import com.r35157.libs.jupiter.perps.JupiterPerpsService;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public final class JupiterPerpsEntryPriceVariableRefresher
        implements AutoCloseable {

    public JupiterPerpsEntryPriceVariableRefresher(
            Map<String, String> variables,
            JupiterPerpsService jupiterPerpsService,
            ΩSolanaWalletIdΩ walletAddress
    ) {
        this.variables = Objects.requireNonNull(variables, "variables");
        this.jupiterPerpsService = Objects.requireNonNull(jupiterPerpsService, "jupiterPerpsService");
        this.walletAddress = Objects.requireNonNull(walletAddress, "walletAddress");

        scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
            Thread thread = new Thread(
                    runnable,
                    "jupiter-perps-price-variable-refresher"
            );

            thread.setDaemon(true);

            return thread;
        });
    }

    public void startPeriodicRefresh() {
        scheduler.scheduleWithFixedDelay(
                this::refreshPeriodically,
                REFRESH_INTERVAL_SECONDS,
                REFRESH_INTERVAL_SECONDS,
                TimeUnit.SECONDS
        );

        System.out.println(
                "Periodic Jupiter Perps price variable refresh started with interval: "
                        + REFRESH_INTERVAL_SECONDS
                        + " seconds"
        );
    }

    @Override
    public void close() {
        scheduler.shutdownNow();
    }

    public synchronized void refresh() {
        System.out.println("Executing refresh now...");

        try {
            System.out.println("   Fetching 'Open Positions':");
            Set<JupiterPerpsPosition> positions = jupiterPerpsService.getOpenPositions(walletAddress);

            System.out.println(
                    "Fetched " + positions.size()
                            + " open Jupiter Perps positions for wallet: " + walletAddress
            );


            Map<String, String> previousPriceVariables = copyPriceVariables();

            removePriceVariables();

            for (JupiterPerpsPosition position : positions) {
                String entryPriceVariableName = createEntryPriceVariableName(position);

                putPriceVariable(
                        previousPriceVariables,
                        entryPriceVariableName,
                        position.entryPrice().toPlainString()
                );

                String liquidationPriceVariableName = createLiquidationPriceVariableName(position);

                putPriceVariable(
                        previousPriceVariables,
                        liquidationPriceVariableName,
                        position.liquidationPrice().toPlainString()
                );

            }
        } catch (IOException |
                 InterruptedException exception) {
            if (exception instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }

            System.err.println(
                    "Could not refresh Jupiter Perps price variables: " + exception.getMessage()
            );
        }
    }

    private Map<String, String> copyPriceVariables() {
        Map<String, String> previousPriceVariables = new HashMap<>();

        for (String variableName : PRICE_VARIABLE_NAMES) {
            String value = variables.get(variableName);

            if (value != null) {
                previousPriceVariables.put(variableName, value);
            }
        }

        return previousPriceVariables;
    }

    private void removePriceVariables() {
        for (String variableName : PRICE_VARIABLE_NAMES) {
            variables.remove(variableName);
        }
    }

    private void putPriceVariable(
            Map<String, String> previousPriceVariables,
            String variableName,
            String variableValue
    ) {
        String previousValue = previousPriceVariables.get(variableName);

        variables.put(variableName, variableValue);

        if (previousValue == null || !previousValue.equals(variableValue)) {
            System.out.println(
                    "{{" + variableName + "}} updated from " +
                            previousValue +
                            " --> " +
                            variableValue
            );
        }
    }

    private static String createEntryPriceVariableName(JupiterPerpsPosition position) {
        return createPriceVariableName(position, "ENTRY_PRICE");
    }

    private static String createLiquidationPriceVariableName(JupiterPerpsPosition position) {
        return createPriceVariableName(position, "LIQ_PRICE");
    }

    private static String createPriceVariableName(
        JupiterPerpsPosition position,
        String suffix
    ) {
        String asset = switch (position.tradedTokenMint()) {
            case "So11111111111111111111111111111111111111112" -> "SOL";
            case "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh" -> "BTC";
            case "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs" -> "ETH";
            default -> throw new IllegalArgumentException(
                    "Unsupported Jupiter Perps traded token mint: " + position.tradedTokenMint()
            );
        };

        return asset + "_" + position.direction() + "_" + suffix;
    }

    private static final Set<String> PRICE_VARIABLE_NAMES = Set.of(
            "SOL_LONG_ENTRY_PRICE",
            "SOL_SHORT_ENTRY_PRICE",

            "BTC_LONG_ENTRY_PRICE",
            "BTC_SHORT_ENTRY_PRICE",

            "ETH_LONG_ENTRY_PRICE",
            "ETH_SHORT_ENTRY_PRICE",

            "SOL_LONG_LIQ_PRICE",
            "SOL_SHORT_LIQ_PRICE",

            "BTC_LONG_LIQ_PRICE",
            "BTC_SHORT_LIQ_PRICE",

            "ETH_LONG_LIQ_PRICE",
            "ETH_SHORT_LIQ_PRICE"
    );

    private void refreshPeriodically() {
        try {
            refresh();
        } catch (RuntimeException exception) {
            System.err.println(
                    "Unexpected error during periodic Jupiter Perps price variable refresh: "
                            + exception.getMessage()
            );

            exception.printStackTrace(System.err);
        }
    }

    private static final ΩsecondsΩ REFRESH_INTERVAL_SECONDS = 60;

    private final ScheduledExecutorService scheduler;
    private final Map<String, String> variables;
    private final JupiterPerpsService jupiterPerpsService;
    private final ΩSolanaWalletIdΩ walletAddress;
}