package socket

import (
	"context"
	"errors"
	"log/slog"

	protoSocket "git.toki-labs.com/toki/proto-socket/go"
	"nhooyr.io/websocket"

	"git.toki-labs.com/toki/alt/services/api/internal/config"
	apiContracts "git.toki-labs.com/toki/alt/services/api/internal/contracts"
	"git.toki-labs.com/toki/alt/services/api/internal/workerclient"
)

const (
	serverName                = "alt-api"
	serverVersion             = "dev"
	defaultAltProtocolVersion = "alt.v1"
)

type Server struct {
	wsServer *protoSocket.WsServer
	worker   workerclient.WorkerClient
}

func NewServer(cfg config.Config) *Server {
	return NewServerWithWorker(cfg, workerclient.New(cfg.WorkerSocketURL))
}

func NewServerWithWorker(cfg config.Config, worker workerclient.WorkerClient) *Server {
	options := protoSocket.WsServerOptions{}
	if len(cfg.WSOriginPatterns) > 0 {
		options.AcceptOptions = &websocket.AcceptOptions{
			OriginPatterns: cfg.WSOriginPatterns,
		}
	}
	wsServer := protoSocket.NewWsServerWithOptions(cfg.Host, cfg.Port, cfg.SocketPath, options, func(conn *websocket.Conn) *protoSocket.WsClient {
		return protoSocket.NewWsClient(conn, cfg.HeartbeatIntervalSec, cfg.HeartbeatWaitSec, apiContracts.ParserMap())
	})
	wsServer.OnClientConnected = registerSessionHandlers(worker)
	return &Server{
		wsServer: wsServer,
		worker:   worker,
	}
}

func (s *Server) Start(ctx context.Context) error {
	go func() {
		if err := s.worker.Connect(ctx); err != nil {
			slog.Warn("eager worker connection failed on startup", "error", err)
		}
	}()
	return s.wsServer.Start(ctx)
}

func (s *Server) Stop() error {
	var errs []error
	if err := s.wsServer.Stop(); err != nil {
		errs = append(errs, err)
	}
	if err := s.worker.Close(); err != nil {
		errs = append(errs, err)
	}
	if len(errs) > 0 {
		return errors.Join(errs...)
	}
	return nil
}
