package contracts

import (
	"google.golang.org/protobuf/proto"

	altv1 "git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1"
	protoSocket "git.toki-labs.com/toki/proto-socket/go"
)

// messageFactories lists every ALT protobuf message the API parser map must
// decode. A single factory list keeps request/response pairs together so a new
// contract message cannot be registered through one code path and missed in
// another, which is the drift this milestone's contract check guards against.
func messageFactories() []func() proto.Message {
	return []func() proto.Message{
		func() proto.Message { return &altv1.HelloRequest{} },
		func() proto.Message { return &altv1.HelloResponse{} },
		// market read surface: instruments and bars
		func() proto.Message { return &altv1.ListInstrumentsRequest{} },
		func() proto.Message { return &altv1.ListInstrumentsResponse{} },
		func() proto.Message { return &altv1.ListBarsRequest{} },
		func() proto.Message { return &altv1.ListBarsResponse{} },
		// market import command: daily bars
		func() proto.Message { return &altv1.ImportDailyBarsRequest{} },
		func() proto.Message { return &altv1.ImportDailyBarsResponse{} },
		// backtest surface: start / list / detail / result / compare
		func() proto.Message { return &altv1.StartBacktestRequest{} },
		func() proto.Message { return &altv1.StartBacktestResponse{} },
		func() proto.Message { return &altv1.GetBacktestRunRequest{} },
		func() proto.Message { return &altv1.GetBacktestRunResponse{} },
		func() proto.Message { return &altv1.GetBacktestResultRequest{} },
		func() proto.Message { return &altv1.GetBacktestResultResponse{} },
		func() proto.Message { return &altv1.BacktestResult{} },
		func() proto.Message { return &altv1.ListBacktestRunsRequest{} },
		func() proto.Message { return &altv1.ListBacktestRunsResponse{} },
		func() proto.Message { return &altv1.GetBacktestRunDetailRequest{} },
		func() proto.Message { return &altv1.GetBacktestRunDetailResponse{} },
		func() proto.Message { return &altv1.CompareBacktestRunsRequest{} },
		func() proto.Message { return &altv1.CompareBacktestRunsResponse{} },
		// paper trading surface: start / state
		func() proto.Message { return &altv1.StartPaperTradingRequest{} },
		func() proto.Message { return &altv1.StartPaperTradingResponse{} },
		func() proto.Message { return &altv1.GetPaperTradingStateRequest{} },
		func() proto.Message { return &altv1.GetPaperTradingStateResponse{} },
		func() proto.Message { return &altv1.PaperTradingState{} },
		// paper order lifecycle surface: submit / cancel / fill
		func() proto.Message { return &altv1.SubmitPaperOrderRequest{} },
		func() proto.Message { return &altv1.SubmitPaperOrderResponse{} },
		func() proto.Message { return &altv1.CancelPaperOrderRequest{} },
		func() proto.Message { return &altv1.CancelPaperOrderResponse{} },
		func() proto.Message { return &altv1.FillPaperOrderRequest{} },
		func() proto.Message { return &altv1.FillPaperOrderResponse{} },
		// live trading surface: broker capability probe
		func() proto.Message { return &altv1.GetLiveBrokerCapabilitiesRequest{} },
		func() proto.Message { return &altv1.GetLiveBrokerCapabilitiesResponse{} },
		func() proto.Message { return &altv1.LiveBrokerCapability{} },
	}
}

// ParserMap returns a fresh ParserMap populated with all ALT API messages.
func ParserMap() protoSocket.ParserMap {
	factories := messageFactories()
	pm := make(protoSocket.ParserMap, len(factories))
	for _, factory := range factories {
		pm[protoSocket.TypeNameOf(factory())] = parserFor(factory)
	}
	return pm
}

func parserFor(factory func() proto.Message) func([]byte) (proto.Message, error) {
	return func(data []byte) (proto.Message, error) {
		msg := factory()
		if err := proto.Unmarshal(data, msg); err != nil {
			return nil, err
		}
		return msg, nil
	}
}
