package socket

import (
	protoSocket "git.toki-labs.com/toki/proto-socket/go"

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

type sessionHandler struct {
	requestType string
	register    func(*protoSocket.WsClient)
}

// sessionHandlers returns the handlers registered on every worker session.
// Hello is always present; market and backtest command/query handlers attach
// through the same registry instead of bespoke registration paths.
func sessionHandlers(deps Deps) []sessionHandler {
	handlers := []sessionHandler{
		helloHandler(deps),
	}
	handlers = append(handlers, marketHandlers(deps)...)
	handlers = append(handlers, backtestHandlers(deps)...)
	handlers = append(handlers, paperHandlers(deps)...)
	handlers = append(handlers, liveHandlers(deps)...)
	return handlers
}

// registerSessionHandlers builds the OnClientConnected callback bound to the
// given dependencies.
func registerSessionHandlers(deps Deps) func(*protoSocket.WsClient) {
	handlers := sessionHandlers(deps)
	return func(client *protoSocket.WsClient) {
		registerHandlers(client, handlers)
	}
}

func registerHandlers(client *protoSocket.WsClient, handlers []sessionHandler) {
	for _, handler := range handlers {
		if handler.register == nil {
			continue
		}
		handler.register(client)
	}
}

func helloHandler(deps Deps) sessionHandler {
	return sessionHandler{
		requestType: protoSocket.TypeNameOf(&altv1.HelloRequest{}),
		register: func(client *protoSocket.WsClient) {
			protoSocket.AddRequestListenerTyped[*altv1.HelloRequest, *altv1.HelloResponse](
				&client.Communicator,
				func(req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
					return handleHello(deps, req)
				},
			)
		},
	}
}

func handleHello(deps Deps, req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
	protocolVersion := req.GetAltProtocolVersion()
	if protocolVersion == "" {
		protocolVersion = defaultAltProtocolVersion
	}
	return &altv1.HelloResponse{
		ServerName:         serverName,
		ServerVersion:      serverVersion,
		AltProtocolVersion: protocolVersion,
		Capabilities:       capabilitiesForDeps(deps),
	}, nil
}

func capabilitiesForDeps(deps Deps) []string {
	caps := []string{"hello"}
	if deps.Instruments != nil && deps.Bars != nil {
		caps = append(caps, "market-read")
	}
	if deps.DailyBarImporter != nil {
		caps = append(caps, "market-import")
	}
	if deps.Analysis != nil && deps.Results != nil {
		caps = append(caps, "backtest-read")
	}
	if deps.Starter != nil {
		caps = append(caps, "backtest-start", "worker-execution")
	}
	if deps.Paper != nil {
		caps = append(caps, "paper-trading")
	}
	if deps.Live != nil {
		caps = append(caps, "live-trading")
	}
	return caps
}
