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"
	"git.toki-labs.com/toki/alt/services/api/internal/workerclient"
)

// sessionHandler is one request-response unit attached to a freshly connected
// client communicator. Splitting registration into independent units keeps the
// api hub control-plane surface thin: later command/query handlers attach
// through the same registry instead of growing a single registration function.
type sessionHandler struct {
	// requestType is the ALT protobuf request type name the handler answers.
	// It lets the registry detect duplicate or missing handler coverage without
	// a live socket connection.
	requestType string
	// register attaches the handler onto the client communicator.
	register func(*protoSocket.WsClient)
}

// sessionHandlers returns the ordered set of handlers registered on every API
// client session. Hello is registered through this registry so its behaviour
// stays identical while gaining the shared registration path.
func sessionHandlers(worker workerclient.WorkerClient) []sessionHandler {
	handlers := []sessionHandler{
		helloHandler(worker),
	}
	handlers = append(handlers, marketHandlers(worker)...)
	handlers = append(handlers, backtestHandlers(worker)...)
	handlers = append(handlers, paperHandlers(worker)...)
	handlers = append(handlers, apiLiveHandlers(worker)...)
	return handlers
}

// registerSessionHandlers wires every session handler onto a newly connected
// client. It is the OnClientConnected entrypoint for the API socket server.
func registerSessionHandlers(worker workerclient.WorkerClient) func(*protoSocket.WsClient) {
	return func(client *protoSocket.WsClient) {
		registerHandlers(client, sessionHandlers(worker))
	}
}

// registerHandlers attaches the given handlers onto a client. nil registrars
// are skipped so a malformed registry entry cannot panic the connection setup
// path.
func registerHandlers(client *protoSocket.WsClient, handlers []sessionHandler) {
	for _, handler := range handlers {
		if handler.register == nil {
			continue
		}
		handler.register(client)
	}
}

func helloHandler(worker workerclient.WorkerClient) 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(worker, req)
			})
		},
	}
}

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

func capabilitiesForSession(worker workerclient.WorkerClient) []string {
	caps := []string{"hello", "request-response", "market-read", "market-import", "backtest-read", "backtest-start", "worker-execution", "paper-trading", "live-trading"}
	if worker != nil && worker.IsConnected() {
		caps = append(caps, "worker-available")
	} else {
		caps = append(caps, "worker-unavailable")
	}
	return caps
}
