package migration

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

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/clientcredentials"
)

// MicrosoftApp mints Graph access tokens via the client credentials flow.
type MicrosoftApp struct {
	clientID      string
	clientSecret  string
	defaultTenant string
	tokenURL      string
	client        *http.Client
}

type MicrosoftAppConfig struct {
	ClientID      string
	ClientSecret  string
	DefaultTenant string
	TokenURL      string
	HTTPClient    *http.Client
}

func NewMicrosoftApp(cfg MicrosoftAppConfig) (*MicrosoftApp, error) {
	clientID := strings.TrimSpace(cfg.ClientID)
	clientSecret := strings.TrimSpace(cfg.ClientSecret)
	if clientID == "" || clientSecret == "" {
		return nil, nil
	}
	client := cfg.HTTPClient
	if client == nil {
		client = &http.Client{Timeout: 30 * time.Second}
	}
	return &MicrosoftApp{
		clientID:      clientID,
		clientSecret:  clientSecret,
		defaultTenant: strings.TrimSpace(cfg.DefaultTenant),
		tokenURL:      strings.TrimSpace(cfg.TokenURL),
		client:        client,
	}, nil
}

func (m *MicrosoftApp) Enabled() bool {
	return m != nil && m.clientID != "" && m.clientSecret != ""
}

func (m *MicrosoftApp) AccessToken(ctx context.Context, tenantID string) (string, error) {
	if !m.Enabled() {
		return "", fmt.Errorf("microsoft app-only auth not configured")
	}
	tenantID = strings.TrimSpace(tenantID)
	if tenantID == "" {
		tenantID = m.defaultTenant
	}
	if tenantID == "" {
		return "", fmt.Errorf("microsoft tenant id required for app-only auth")
	}
	tokenURL := m.tokenURL
	if tokenURL == "" {
		tokenURL = fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", url.PathEscape(tenantID))
	}
	cc := clientcredentials.Config{
		ClientID:     m.clientID,
		ClientSecret: m.clientSecret,
		TokenURL:     tokenURL,
		Scopes:       []string{"https://graph.microsoft.com/.default"},
	}
	if m.client != nil {
		ctx = context.WithValue(ctx, oauth2.HTTPClient, m.client)
	}
	token, err := cc.Token(ctx)
	if err != nil {
		return "", fmt.Errorf("microsoft app token: %w", err)
	}
	if token.AccessToken == "" {
		return "", fmt.Errorf("microsoft app token empty")
	}
	return token.AccessToken, nil
}

// WithClient overrides the HTTP client (tests).
func (m *MicrosoftApp) WithClient(c *http.Client) *MicrosoftApp {
	if m != nil && c != nil {
		m.client = c
	}
	return m
}

func microsoftAppTokenURL(tenantID string) string {
	tenantID = strings.TrimSpace(tenantID)
	if tenantID == "" {
		tenantID = "common"
	}
	return fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", url.PathEscape(tenantID))
}
