package auth

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

	"google.golang.org/grpc"

	"github.com/netbirdio/netbird/proxy/auth"
	"github.com/netbirdio/netbird/proxy/internal/types"
	"github.com/netbirdio/netbird/shared/management/proto"
)

type urlGenerator interface {
	GetOIDCURL(context.Context, *proto.GetOIDCURLRequest, ...grpc.CallOption) (*proto.GetOIDCURLResponse, error)
}

type OIDC struct {
	id             types.ServiceID
	accountId      types.AccountID
	forwardedProto string
	client         urlGenerator
}

// NewOIDC creates a new OIDC authentication scheme
func NewOIDC(client urlGenerator, id types.ServiceID, accountId types.AccountID, forwardedProto string) OIDC {
	return OIDC{
		id:             id,
		accountId:      accountId,
		forwardedProto: forwardedProto,
		client:         client,
	}
}

func (OIDC) Type() auth.Method {
	return auth.MethodOIDC
}

// Authenticate checks for an OIDC session token or obtains the OIDC redirect URL.
func (o OIDC) Authenticate(r *http.Request) (string, string, error) {
	// Check for the session_token query param (from OIDC redirects).
	// The management server passes the token in the URL because it cannot set
	// cookies for the proxy's domain (cookies are domain-scoped per RFC 6265).
	if token := r.URL.Query().Get("session_token"); token != "" {
		return token, "", nil
	}

	redirectURL := &url.URL{
		Scheme: auth.ResolveProto(o.forwardedProto, r.TLS),
		Host:   r.Host,
		Path:   r.URL.Path,
	}

	res, err := o.client.GetOIDCURL(r.Context(), &proto.GetOIDCURLRequest{
		Id:          string(o.id),
		AccountId:   string(o.accountId),
		RedirectUrl: redirectURL.String(),
	})
	if err != nil {
		return "", "", fmt.Errorf("get OIDC URL: %w", err)
	}

	return "", res.GetUrl(), nil
}
