package fallback

import (
	"context"

	"github.com/sagernet/sing-box/adapter"
	C "github.com/sagernet/sing-box/constant"
	"github.com/sagernet/sing-box/dns"
	"github.com/sagernet/sing-box/log"
	"github.com/sagernet/sing-box/option"
	E "github.com/sagernet/sing/common/exceptions"
	"github.com/sagernet/sing/common/logger"
	"github.com/sagernet/sing/service"

	mDNS "github.com/miekg/dns"
)

func RegisterTransport(registry *dns.TransportRegistry) {
	dns.RegisterTransport[option.FallbackDNSServerOptions](registry, C.DNSTypeFallback, NewTransport)
}

var _ adapter.DNSTransport = (*Transport)(nil)

type Transport struct {
	dns.TransportAdapter
	ctx      context.Context
	manager  adapter.DNSTransportManager
	logger   logger.ContextLogger
	tags     []string
	strategy ExchangeStrategy
}

func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.FallbackDNSServerOptions) (adapter.DNSTransport, error) {
	if len(options.Servers) == 0 {
		return nil, E.New("missing servers")
	}
	manager := service.FromContext[adapter.DNSTransportManager](ctx)
	servers := make([]adapter.DNSTransport, len(options.Servers))
	for i, tag := range options.Servers {
		server, loaded := manager.Transport(tag)
		if !loaded {
			return nil, E.New("server ", tag, " not found")
		}
		servers[i] = server
	}
	strategy, err := CreateStrategy(options.Strategy, servers, logger)
	if err != nil {
		return nil, err
	}
	return &Transport{
		TransportAdapter: dns.NewTransportAdapter(C.DNSTypeFallback, tag, options.Servers),
		ctx:              ctx,
		logger:           logger,
		tags:             options.Servers,
		strategy:         strategy,
	}, nil
}

func (t *Transport) Start(stage adapter.StartStage) error {
	return nil
}

func (t *Transport) Close() error {
	return nil
}

func (t *Transport) Reset() {
}

func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
	return t.strategy(ctx, message)
}
