import com.cryptostruct.commons.OrderSide;
import com.cryptostruct.commons.Price;
import com.cryptostruct.commons.Quantity;
import com.cryptostruct.commons.annotations.ExportStrategy;
import com.cryptostruct.commons.annotations.Parameter;
import com.cryptostruct.sdk.StopReason;
import com.cryptostruct.sdk.Strategy;
import com.cryptostruct.sdk.StrategyContext;
import com.cryptostruct.sdk.StrategyTimer;
import com.cryptostruct.sdk.instruments.DataInstrument;
import com.cryptostruct.sdk.instruments.TradingInstrument;
import com.cryptostruct.sdk.orders.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@ExportStrategy(name = "ExampleStrategy")
public class ExampleStrategy implements Strategy {
    @Parameter
    private TradingInstrument tradingInstrument;

    private Order bidOrder;
    private Order askOrder;

    @Parameter(optional = true)
    private Price diffToToB = Price.valueOf(15);

    private StrategyContext context;

    public ExampleStrategy(StrategyContext context) {
        this.context = context;
    }

    @Override
    public void onStarted() {
        log.info("onStarted()");
    }

    @Override
    public void onStop(StopReason stopReason, String message) {
        log.info("onStop(): {}", message);
    }

    @Override
    public void onMarketDataState(DataInstrument instrument) {
        log.info("onMarketDataState()");
    }

    @Override
    public void onInstrumentSnapshot(DataInstrument instrument) {
        log.info("onInstrumentSnapshot()");
        instrument.getSnapshot().printBook(5, log::info);

        if((bidOrder != null && bidOrder.getState() == OrderState.FILLED)
        && (askOrder != null && askOrder.getState() == OrderState.FILLED)) {
            // reset orders, to be able to place new
            bidOrder = null;
            askOrder = null;
        }

        if(bidOrder == null) {
            bidOrder = tradingInstrument.placeOrder(
                    OrderParams
                            .builder()
                            .price(instrument.getSnapshot().getTopOfBook().getBid().getPrice().subtract(diffToToB))
                            .quantity(Quantity.valueOf(100))
                            .side(OrderSide.BID)
                            .type(OrderType.LIMIT)
                    .build()
            );
        } else {
            Price tobBidPrice = instrument.getSnapshot().getTopOfBook().getBid().getPrice().subtract(diffToToB);

            if(!tobBidPrice.equals(bidOrder.getPrice())) {
                bidOrder.modifyPrice(tobBidPrice);
            }
        }

        if(askOrder == null) {
            askOrder = tradingInstrument.placeOrder(
                    OrderParams
                        .builder()
                        .price(instrument.getSnapshot().getTopOfBook().getAsk().getPrice().add(diffToToB))
                            .quantity(Quantity.valueOf(100))
                            .type(OrderType.LIMIT)
                            .side(OrderSide.ASK)
                    .build()
            );
        } else {
            Price tobAskPrice = instrument.getSnapshot().getTopOfBook().getAsk().getPrice().add(diffToToB);

            if(!tobAskPrice.equals(askOrder.getPrice())) {
                askOrder.modifyPrice(tobAskPrice);
            }
        }
    }

    @Override
    public void onInstrumentTrades(DataInstrument instrument) {
        log.info("onInstrumentTrades()");
        instrument.getLastTrades().forEach(trade -> log.info(trade.toString()));
    }

    @Override
    public void onInstrumentPosition(TradingInstrument instrument) {
        log.info("onInstrumentPosition()");
    }

    @Override
    public void onOrderFill(Order order) {
        log.info("onOrderFill: {}", order.getDescriptiveString());
    }

    @Override
    public void onOrderUpdate(Order order) {
        log.info("onOrderUpdate(): {}", order.getDescriptiveString());
    }

    @Override
    public void onOrderError(Order order, OrderError orderError) {
        log.error("onOrderError(): {}", orderError.getMessage());
    }

    @Override
    public void onHeartbeat() {

    }

    @Override
    public void onTimer(StrategyTimer timer) {

    }
}
