package ru.tinkoff.invest.openapi.models.streaming;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.tinkoff.invest.openapi.models.market.CandleInterval;

import java.util.Objects;

/**
 * Общий класс для моделей запросов на подписки в streaming.
 */
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="event")
@JsonSubTypes({
        @JsonSubTypes.Type(value = StreamingRequest.CandleSubscribeRequest.class, name = "candle:subscribe"),
        @JsonSubTypes.Type(value = StreamingRequest.CandleUnsubscribeRequest.class, name = "candle:unsubscribe"),
        @JsonSubTypes.Type(value = StreamingRequest.OrderbookSubscribeRequest.class, name = "orderbook:subscribe"),
        @JsonSubTypes.Type(value = StreamingRequest.OrderbookUnsubscribeRequest.class, name = "orderbook:unsubscribe"),
        @JsonSubTypes.Type(value = StreamingRequest.InstrumentInfoSubscribeRequest.class, name = "instrument_info:subscribe"),
        @JsonSubTypes.Type(value = StreamingRequest.InstrumentInfoUnsubscribeRequest.class, name = "instrument_info:unsubscribe")
})
public abstract class StreamingRequest {

    /**
     * Идентификатор подписки.
     */
    protected final String requestId;

    protected StreamingRequest(@Nullable final String requestId) {
        this.requestId = requestId;
    }

    @Nullable
    public String getRequestId() {
        return requestId;
    }

    @NotNull
    public abstract String onOffPairId();

    public static abstract class ActivatingRequest extends StreamingRequest {
        protected ActivatingRequest(String requestId) {
            super(requestId);
        }
    }
    public static abstract class DeactivatingRequest extends StreamingRequest {
        protected DeactivatingRequest(String requestId) {
            super(requestId);
        }
    }

    /**
     * Запрос на подписку на поток событий о свечах.
     */
    public static class CandleSubscribeRequest extends ActivatingRequest {

        /**
         * Идентификатор инструмента.
         */
        private final String figi;

        /**
         * Временной интервал свечей.
         */
        private final CandleInterval interval;

        CandleSubscribeRequest(@NotNull final String figi,
                               @NotNull final CandleInterval interval,
                               @Nullable final String requestId) {
            super(requestId);
            this.figi = figi;
            this.interval = interval;
        }

        CandleSubscribeRequest(@NotNull final String figi,
                               @NotNull final CandleInterval interval) {
            this(figi, interval, null);
        }

        @NotNull
        public String getFigi() {
            return figi;
        }

        @NotNull
        public CandleInterval getInterval() {
            return interval;
        }

        @NotNull
        @Override
        public String onOffPairId() {
            return new StringBuilder("Candle(")
                .append(figi)
                .append(",")
                .append(interval.name())
                .append(")")
                .toString();
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("CandleSubscribeRequest(");
            if (Objects.nonNull(requestId)) sb.append("requestId='").append(requestId).append('\'');
            sb.append(", figi='").append(figi).append('\'');
            sb.append(", interval=").append(interval);
            sb.append(')');
            return sb.toString();
        }
    }

    /**
     * Запрос на отписку от потока событий о свечах.
     */
    public static class CandleUnsubscribeRequest extends DeactivatingRequest {

        /**
         * Идентификатор инструмента.
         */
        private final String figi;

        /**
         * Временной интервал свечей.
         */
        private final CandleInterval interval;

        CandleUnsubscribeRequest(@NotNull final String figi,
                                 @NotNull final CandleInterval interval,
                                 @Nullable final String requestId) {
            super(requestId);
            this.figi = figi;
            this.interval = interval;
        }

        CandleUnsubscribeRequest(@NotNull final String figi,
                                 @NotNull final CandleInterval interval) {
            this(figi, interval, null);
        }

        @NotNull
        public String getFigi() {
            return figi;
        }

        @NotNull
        public CandleInterval getInterval() {
            return interval;
        }

        @NotNull
        @Override
        public String onOffPairId() {
            return new StringBuilder("Candle(")
                    .append(figi)
                    .append(",")
                    .append(interval.name())
                    .append(")")
                    .toString();
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("CandleUnsubscribeRequest(");
            if (Objects.nonNull(requestId)) sb.append("requestId='").append(requestId).append('\'');
            sb.append(", figi='").append(figi).append('\'');
            sb.append(", interval=").append(interval);
            sb.append(')');
            return sb.toString();
        }
    }

    /**
     * Запрос на подписку на поток событий об изменениях в инструменте.
     */
    public static class InstrumentInfoSubscribeRequest extends ActivatingRequest {

        /**
         * Идентификатор инструмента.
         */
        private final String figi;

        InstrumentInfoSubscribeRequest(@NotNull final String figi,
                                       @Nullable final String requestId) {
            super(requestId);
            this.figi = figi;
        }

        InstrumentInfoSubscribeRequest(@NotNull final String figi) {
            this(figi,null);
        }

        @NotNull
        public String getFigi() {
            return figi;
        }

        @NotNull
        @Override
        public String onOffPairId() {
            return new StringBuilder("InstrumentInfo(")
                    .append(figi)
                    .append(")")
                    .toString();
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("InstrumentInfoSubscribeRequest(");
            if (Objects.nonNull(requestId)) sb.append("requestId='").append(requestId).append('\'');
            sb.append(", figi='").append(figi).append('\'');
            sb.append(')');
            return sb.toString();
        }

    }

    /**
     * Запрос на отписку от потока событий об изменениях в инструменте.
     */
    public static class InstrumentInfoUnsubscribeRequest extends DeactivatingRequest {

        /**
         * Идентификатор инструмента.
         */
        private final String figi;

        InstrumentInfoUnsubscribeRequest(@NotNull final String figi,
                                         @Nullable final String requestId) {
            super(requestId);
            this.figi = figi;
        }

        InstrumentInfoUnsubscribeRequest(@NotNull final String figi) {
            this(figi, null);
        }

        @NotNull
        public String getFigi() {
            return figi;
        }

        @NotNull
        @Override
        public String onOffPairId() {
            return new StringBuilder("InstrumentInfo(")
                    .append(figi)
                    .append(")")
                    .toString();
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("InstrumentInfoUnsubscribeRequest(");
            if (Objects.nonNull(requestId)) sb.append("requestId='").append(requestId).append('\'');
            sb.append(", figi='").append(figi).append('\'');
            sb.append(')');
            return sb.toString();
        }

    }

    /**
     * Запрос на подписку на поток событий об изменениях биржевого стакана.
     */
    public static class OrderbookSubscribeRequest extends ActivatingRequest {

        /**
         * Идентификатор инструмента.
         */
        private final String figi;

        /**
         * Глубина стакана.
         */
        private final int depth;

        OrderbookSubscribeRequest(@NotNull final String figi,
                                  final int depth,
                                  @Nullable final String requestId) {
            super(requestId);
            if (depth < 0  || depth > 20) throw new IllegalArgumentException("Глубина должна быть от 1 до 20");
            this.figi = figi;
            this.depth = depth;
        }

        OrderbookSubscribeRequest(@NotNull final String figi,
                                  final int depth) {
            this(figi, depth, null);
        }

        @NotNull
        public String getFigi() {
            return figi;
        }

        public int getDepth() {
            return depth;
        }

        @NotNull
        @Override
        public String onOffPairId() {
            return new StringBuilder("Orderbook(")
                    .append(figi)
                    .append(",")
                    .append(depth)
                    .append(")")
                    .toString();
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("OrderbookSubscribeRequest(");
            if (Objects.nonNull(requestId)) sb.append("requestId='").append(requestId).append('\'');
            sb.append(", figi='").append(figi).append('\'');
            sb.append(", depth=").append(depth);
            sb.append(')');
            return sb.toString();
        }

    }

    /**
     * Запрос на отписку от потока событий об изменениях биржевого стакана.
     */
    public static class OrderbookUnsubscribeRequest extends DeactivatingRequest {

        /**
         * Идентификатор инструмента.
         */
        private final String figi;

        /**
         * Глубина стакана.
         */
        private final int depth;

        OrderbookUnsubscribeRequest(@NotNull final String figi,
                                    final int depth,
                                    @Nullable final String requestId) {
            super(requestId);
            if (depth < 0  || depth > 20) throw new IllegalArgumentException("Глубина должна быть от 1 до 20");
            this.figi = figi;
            this.depth = depth;
        }

        OrderbookUnsubscribeRequest(@NotNull final String figi,
                                    final int depth) {
            this(figi, depth, null);
        }

        @NotNull
        public String getFigi() {
            return figi;
        }

        public int getDepth() {
            return depth;
        }

        @NotNull
        @Override
        public String onOffPairId() {
            return new StringBuilder("Orderbook(")
                    .append(figi)
                    .append(",")
                    .append(depth)
                    .append(")")
                    .toString();
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("OrderbookUnsubscribeRequest(");
            if (Objects.nonNull(requestId)) sb.append("requestId='").append(requestId).append('\'');
            sb.append(", figi='").append(figi).append('\'');
            sb.append(", depth=").append(depth);
            sb.append(')');
            return sb.toString();
        }

    }

    public static CandleSubscribeRequest subscribeCandle(@NotNull final String figi,
                                                         @NotNull final CandleInterval interval) {
        return new StreamingRequest.CandleSubscribeRequest(figi, interval);
    }

    public static CandleUnsubscribeRequest unsubscribeCandle(@NotNull final String figi,
                                                             @NotNull final CandleInterval interval) {
        return new StreamingRequest.CandleUnsubscribeRequest(figi, interval);
    }

    public static OrderbookSubscribeRequest subscribeOrderbook(@NotNull final String figi, final int depth) {
        return new StreamingRequest.OrderbookSubscribeRequest(figi, depth);
    }

    public static OrderbookUnsubscribeRequest unsubscribeOrderbook(@NotNull final String figi, final int depth) {
        return new StreamingRequest.OrderbookUnsubscribeRequest(figi, depth);
    }

    public static InstrumentInfoSubscribeRequest subscribeInstrumentInfo(@NotNull final String figi) {
        return new StreamingRequest.InstrumentInfoSubscribeRequest(figi);
    }

    public static InstrumentInfoUnsubscribeRequest unsubscribeInstrumentInfo(@NotNull final String figi) {
        return new StreamingRequest.InstrumentInfoUnsubscribeRequest(figi);
    }
}