//go:build with_usbip && (linux || (darwin && cgo) || windows)

package usbip

import (
	"context"
	"net"

	"github.com/sagernet/sing-box/adapter"
	boxService "github.com/sagernet/sing-box/adapter/service"
	"github.com/sagernet/sing-box/common/listener"
	C "github.com/sagernet/sing-box/constant"
	"github.com/sagernet/sing-box/log"
	"github.com/sagernet/sing-box/option"
	"github.com/sagernet/sing-usbip"
	E "github.com/sagernet/sing/common/exceptions"
	N "github.com/sagernet/sing/common/network"
)

type ServerService struct {
	boxService.Adapter
	ctx    context.Context
	logger log.ContextLogger
	inner  *usbip.ServerService
}

type dynamicServerService struct {
	ServerService
	host *usbip.DynamicHost
}

var _ adapter.USBIPDynamicServer = (*dynamicServerService)(nil)

func NewServerService(ctx context.Context, logger log.ContextLogger, tag string, options option.USBIPServerServiceOptions) (adapter.Service, error) {
	listenOptions := options.ListenOptions
	if listenOptions.ListenPort == 0 {
		listenOptions.ListenPort = usbip.DefaultPort
	}
	boxListener := listener.New(listener.Options{
		Context: ctx,
		Logger:  logger,
		Network: []string{N.NetworkTCP},
		Listen:  listenOptions,
	})
	serverOptions := usbip.ServerOptions{
		Logger: logger,
		Listen: func(context.Context) (net.Listener, error) {
			return boxListener.ListenTCP()
		},
	}
	base := ServerService{
		Adapter: boxService.NewAdapter(C.TypeUSBIPServer, tag),
		ctx:     ctx,
		logger:  logger,
	}

	providerType := options.Provider
	if providerType == "" {
		providerType = option.USBIPProviderDefault
	}
	switch providerType {
	case option.USBIPProviderDefault:
		defaultOptions, isDefault := options.Options.(*option.USBIPDefaultProviderOptions)
		if isDefault {
			serverOptions.Devices = toDeviceMatches(defaultOptions.Devices)
		}
		inner, err := usbip.NewServerService(ctx, serverOptions)
		if err != nil {
			return nil, err
		}
		base.inner = inner
		return &base, nil
	case option.USBIPProviderDynamic:
		host := usbip.NewDynamicHost(logger)
		inner, err := usbip.NewDynamicServerService(ctx, serverOptions, host)
		if err != nil {
			return nil, err
		}
		base.inner = inner
		return &dynamicServerService{ServerService: base, host: host}, nil
	default:
		return nil, E.New("unknown usbip provider type: ", providerType)
	}
}

func (s *ServerService) Start(stage adapter.StartStage) error {
	if stage != adapter.StartStateStart {
		return nil
	}
	return s.inner.Start()
}

func (s *ServerService) Close() error {
	return s.inner.Close()
}

func (s *dynamicServerService) AddDevice(info usbip.ProvidedDeviceInfo, transport usbip.DeviceTransport) (string, error) {
	return s.host.AddDevice(info, transport)
}

func (s *dynamicServerService) RemoveDevice(busID string) {
	s.host.RemoveDevice(busID)
}

func (s *dynamicServerService) SubscribeDevices(ctx context.Context, listener func([]usbip.ControlDeviceInfo)) {
	s.inner.SubscribeDevices(ctx, listener)
}
