package com.r35157.jupiterperpsalarm.impl.ref;

import com.r35157.cryptowallet.solana.SolanaWallet;
import com.r35157.libs.jupiter.JupiterTransactionOutcomeException;
import com.r35157.libs.jupiter.perps.JupiterPerpsService;
import com.r35157.libs.valuetypes.basic.MoneyAmount;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Objects;

import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.SPL_TOKEN_PROGRAM;

public final class JupiterPerpsPositionIncreaseAlarmAction implements ConfiguredAlarmAction {
    public JupiterPerpsPositionIncreaseAlarmAction(
            JupiterPerpsPositionIncreaseAlarmActionConfiguration configuration,
            SolanaWallet wallet,
            JupiterPerpsService jupiter,
            AlarmVariableResolver variableResolver
    ) {
        this.configuration = Objects.requireNonNull(
                configuration,
                "configuration"
        );
        this.wallet = Objects.requireNonNull(wallet, "wallet");
        this.jupiter = Objects.requireNonNull(jupiter, "jupiter");
        this.variableResolver = Objects.requireNonNull(variableResolver, "variableResolver");
    }

    @Override
    public String getConfigurationFileName() {
        return CONFIGURATION_FILE_NAME;
    }

    @Override
    public void trigger(OraclePrice price, ResolvedPriceAlarm alarm) {
        JupiterPerpsPositionIncreaseAlarmActionConfiguration.PositionIncrease definition =
                configuration.positionIncreases().get(alarm.id());
        if (definition == null) {
            return;
        }

        try {
            JupiterPerpsPositionIncreaseAlarmActionConfiguration.ResolvedPositionIncrease
                    positionIncrease = definition.resolve(variableResolver);

            if (!isUSDCBalanceOk(positionIncrease)) {
                return;
            }

            ΩSolanaTransactionSignatureΩ signature =
                    jupiter.executePositionIncrease(
                            positionIncrease.asset().tradedTokenMint(),
                            positionIncrease.direction(),
                            positionIncrease.collateralUsd(),
                            positionIncrease.sizeUsdDelta(),
                            positionIncrease.maxSlippageBps()
                    );

            System.out.println("Position Increase Signature: " + signature);
        } catch (JupiterTransactionOutcomeException e) {
            reportTransactionOutcome(e);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println(
                    "Jupiter Perps position increase was interrupted"
            );
        } catch(Exception e) {
            System.err.println("EXCEPTION: " + e.getMessage());
        }
    }

    private static void reportTransactionOutcome(
            JupiterTransactionOutcomeException exception
    ) {
        switch (exception.outcome().status()) {
            case FAILED -> System.err.println(
                    "Jupiter Perps position increase failed on-chain: "
                            + "transaction "
                            + exception.transactionSignature()
                            + ", confirmation slot "
                            + exception.outcome().slot()
                            + ", Solana error "
                            + exception.outcome().failureDetails()
            );
            case TIMED_OUT -> System.err.println(
                    "Jupiter Perps position increase transaction "
                            + exception.transactionSignature()
                            + " has an unknown on-chain outcome after timeout; "
                            + "no equivalent transaction was automatically "
                            + "resubmitted"
            );
            case SUCCEEDED -> throw new IllegalStateException(
                    "A successful transaction outcome is not exceptional"
            );
        }
    }

    private boolean isUSDCBalanceOk(JupiterPerpsPositionIncreaseAlarmActionConfiguration.ResolvedPositionIncrease positionIncrease)
            throws IOException, InterruptedException {
        ΩAmountΩ usdcBalance = wallet.getSPLTokenBalance(
                JupiterPerpsReceiveToken.USDC.receiveTokenMint(),
                SPL_TOKEN_PROGRAM
        );

        if (usdcBalance == null) {
            System.err.println("Position Increase of "
                    + positionIncrease.collateralUsd()
                    + " USDC skipped: Wallet has no USDC token account!"
            );
            return false;
        }

        ΩUSDCAmountΩ collateralUsd = positionIncrease.collateralUsd();
        ΩUSDCAmountΩ reserveUsdcLimit = configuration.reserveUsdcLimit();
        ΩUSDCAmountΩ requiredUsdcBalance = collateralUsd.add(reserveUsdcLimit);

        if (usdcBalance.compareTo(requiredUsdcBalance) < 0) {
            System.err.println(
                    "Position Increase of "
                            + positionIncrease.collateralUsd()
                            + " USDC skipped: Wallet has "
                            + usdcBalance
                            + " USDC, but "
                            + requiredUsdcBalance
                            + " USDC is required ("
                            + collateralUsd
                            + " collateral + "
                            + reserveUsdcLimit
                            + " reserve)."
            );
            return false;
        }

        return true;
    }

    private final JupiterPerpsPositionIncreaseAlarmActionConfiguration configuration;
    private final SolanaWallet wallet;
    private final JupiterPerpsService jupiter;
    private final AlarmVariableResolver variableResolver;

    static final String CONFIGURATION_FILE_NAME =
            "alarmaction_JupiterPerpsPositionIncreaseAlarmAction.conf";
}
