package azure

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/pingcap-inc/pchub/pkg/log"
)

// AzureUser represents an Azure AD user
type AzureUser struct {
	ObjectId          string
	Email             string
	UserPrincipalName string
}

// GraphUsersResponse represents the response from Microsoft Graph API
type GraphUsersResponse struct {
	ODataContext  string      `json:"@odata.context"`
	ODataNextLink string      `json:"@odata.nextLink"`
	Value         []GraphUser `json:"value"`
}

// GraphUser represents a user from Microsoft Graph API response
type GraphUser struct {
	ID                string `json:"id"`
	Mail              string `json:"mail"`
	UserPrincipalName string `json:"userPrincipalName"`
	DisplayName       string `json:"displayName"`
	AccountEnabled    bool   `json:"accountEnabled"`
}

// AzureTokenResponse Azure Token 响应结构
type AzureTokenResponse struct {
	TokenType   string `json:"token_type"`
	ExpiresIn   int    `json:"expires_in"`
	AccessToken string `json:"access_token"`
}

// GetAzureAccessToken 获取 Azure Access Token
func GetAzureAccessToken(ctx context.Context, tenantID, clientID, clientSecret string) (string, error) {
	tokenURL := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantID)

	data := url.Values{}
	data.Set("grant_type", "client_credentials")
	data.Set("client_id", clientID)
	data.Set("client_secret", clientSecret)
	data.Set("scope", "https://graph.microsoft.com/.default")

	req, err := http.NewRequestWithContext(ctx, "POST", tokenURL, strings.NewReader(data.Encode()))
	if err != nil {
		return "", fmt.Errorf("failed to create token request: %w", err)
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("failed to get token: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("token request failed with status %d: %s", resp.StatusCode, string(body))
	}

	var tokenResp AzureTokenResponse
	if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
		return "", fmt.Errorf("failed to decode token response: %w", err)
	}

	return tokenResp.AccessToken, nil
}

// GetAzureUserByEmail 通过邮箱获取单个用户的 Azure Object ID
func GetAzureUserByEmail(ctx context.Context, email, accessToken string) (string, error) {
	// 使用 Microsoft Graph API 查找用户
	graphURL := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/%s", url.QueryEscape(email))

	req, err := http.NewRequestWithContext(ctx, "GET", graphURL, nil)
	if err != nil {
		return "", fmt.Errorf("failed to create graph request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("failed to call graph api: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNotFound {
		return "", fmt.Errorf("user not found: %s", email)
	}

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("graph api request failed with status %d: %s", resp.StatusCode, string(body))
	}

	var user GraphUser
	if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
		return "", fmt.Errorf("failed to decode graph response: %w", err)
	}

	return user.ID, nil
}

// GetAzureUserObjectIDs 通过邮箱获取Azure用户的Object ID
func GetAzureUserObjectIDs(ctx context.Context, emails []string, tenantID, clientID, clientSecret string) (map[string]string, error) {
	if len(emails) == 0 {
		return make(map[string]string), nil
	}

	// 获取 Access Token
	accessToken, err := GetAzureAccessToken(ctx, tenantID, clientID, clientSecret)
	if err != nil {
		return nil, fmt.Errorf("failed to get access token: %w", err)
	}

	userObjectIDs := make(map[string]string)

	// 为每个邮箱获取 Object ID
	for _, email := range emails {
		objectID, err := GetAzureUserByEmail(ctx, email, accessToken)
		if err != nil {
			log.L(ctx).Warnf("Failed to get Azure Object ID for user %s: %v", email, err)
			// 如果获取失败，可以选择跳过这个用户或者返回错误
			// 这里选择跳过，但记录警告
			continue
		}

		userObjectIDs[email] = objectID
		log.L(ctx).Infof("Successfully got Azure Object ID for user %s: %s", email, objectID)
	}

	return userObjectIDs, nil
}

// BuildAzureRoleDefinitionPath 构建完整的 Azure 角色定义路径
func BuildAzureRoleDefinitionPath(subscriptionID, roleGUID string) string {
	return fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Authorization/roleDefinitions/%s", subscriptionID, roleGUID)
}
