package cilogs

import (
	"context"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/bluesky-social/indigo/atproto/identity"
	"github.com/bluesky-social/indigo/atproto/syntax"
	indigoxrpc "github.com/bluesky-social/indigo/xrpc"
	"github.com/gorilla/websocket"
	"tangled.org/core/api/org_tangled"
	"tangled.org/core/hostutil"
	extlexutil "tangled.org/core/lexutil"
	"tangled.org/core/spindle/knotclient"
)

var ErrNoSpindle = errors.New("no spindle configured for this repo")

type Spindles struct {
	knots  *knotclient.Client
	http   *http.Client
	dialer *net.Dialer
}

// the manifest can name any host, so outside dev the calls refuse special addresses
func NewSpindles(dir identity.Directory, dev bool) *Spindles {
	httpc := knotclient.HTTPClient(dev)
	return &Spindles{knots: knotclient.New(dir, httpc, dev), http: httpc, dialer: knotclient.Dialer(dev)}
}

func (s *Spindles) Of(ctx context.Context, repo syntax.DID) (*extlexutil.Client, error) {
	knot, err := s.knots.KnotOf(ctx, repo)
	if err != nil {
		return nil, err
	}
	var manifest org_tangled.RepoManifest
	if err := s.knots.GetRecord(ctx, knot, repo, org_tangled.RepoManifestNSID, knotclient.ManifestRkey, &manifest); err != nil {
		return nil, err
	}
	if manifest.Spindle == nil || *manifest.Spindle == "" {
		return nil, ErrNoSpindle
	}
	base, err := spindleURL(*manifest.Spindle)
	if err != nil {
		return nil, err
	}
	return &extlexutil.Client{
		Client: indigoxrpc.Client{Host: base, Client: s.http},
		Dialer: websocket.Dialer{NetDialContext: s.dialer.DialContext},
	}, nil
}

// the spindle serves xrpc on the did:web host, like hostForKnot in web
func spindleURL(did string) (string, error) {
	host, ok := strings.CutPrefix(did, "did:web:")
	if !ok || strings.Contains(host, ":") {
		return "", fmt.Errorf("spindle %q is not a did:web host", did)
	}
	host, err := url.PathUnescape(host)
	if err != nil {
		return "", fmt.Errorf("spindle %q: %w", did, err)
	}
	return hostutil.EnsureHttpScheme(host)
}
