package contracts

import (
	"testing"

	"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"
)

// requiredAPIMessages is the milestone contract surface the API parser map must
// cover. It is declared independently from messageFactories so that removing a
// message from the parser map is caught here as a missing parser instead of
// silently shrinking both sides together.
func requiredAPIMessages() []proto.Message {
	return []proto.Message{
		&altv1.HelloRequest{},
		&altv1.HelloResponse{},
		// market instruments / bars
		&altv1.ListInstrumentsRequest{},
		&altv1.ListInstrumentsResponse{},
		&altv1.ListBarsRequest{},
		&altv1.ListBarsResponse{},
		// market import command: daily bars
		&altv1.ImportDailyBarsRequest{},
		&altv1.ImportDailyBarsResponse{},
		// backtest start / list / detail / result / compare
		&altv1.StartBacktestRequest{},
		&altv1.StartBacktestResponse{},
		&altv1.GetBacktestRunRequest{},
		&altv1.GetBacktestRunResponse{},
		&altv1.GetBacktestResultRequest{},
		&altv1.GetBacktestResultResponse{},
		&altv1.BacktestResult{},
		&altv1.ListBacktestRunsRequest{},
		&altv1.ListBacktestRunsResponse{},
		&altv1.GetBacktestRunDetailRequest{},
		&altv1.GetBacktestRunDetailResponse{},
		&altv1.CompareBacktestRunsRequest{},
		&altv1.CompareBacktestRunsResponse{},
		// paper trading start / state
		&altv1.StartPaperTradingRequest{},
		&altv1.StartPaperTradingResponse{},
		&altv1.GetPaperTradingStateRequest{},
		&altv1.GetPaperTradingStateResponse{},
		&altv1.PaperTradingState{},
	}
}

func TestParserMapIncludesAltMessages(t *testing.T) {
	pm := ParserMap()

	for _, msg := range requiredAPIMessages() {
		typeName := protoSocket.TypeNameOf(msg)
		t.Run(typeName, func(t *testing.T) {
			parser := pm[typeName]
			if parser == nil {
				t.Fatalf("parser not registered for type %s", typeName)
			}

			data, err := proto.Marshal(msg)
			if err != nil {
				t.Fatalf("failed to marshal message: %v", err)
			}

			parsed, err := parser(data)
			if err != nil {
				t.Fatalf("failed to parse message: %v", err)
			}

			if protoSocket.TypeNameOf(parsed) != typeName {
				t.Errorf("parsed type mismatch: expected %s, got %s", typeName, protoSocket.TypeNameOf(parsed))
			}
		})
	}
}

// TestParserMapReportsMissingParser confirms a missing parser is observable as
// a nil lookup, so the coverage assertions above fail loudly when a required
// message is dropped from the parser map.
func TestParserMapReportsMissingParser(t *testing.T) {
	pm := ParserMap()

	if parser := pm["alt.v1.UnregisteredProbeMessage"]; parser != nil {
		t.Fatal("expected nil parser for an unregistered message type")
	}
}
