package azure

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

	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization"

	"github.com/pingcap-inc/pchub/internal/apiserver/service/v1/assume_role"
	modelv1 "github.com/pingcap-inc/pchub/model/v1"
	"github.com/pingcap-inc/pchub/pkg/log"
)

// getAzureProviderMembers 获取特定Azure提供者的成员
func (p *Provider) getAzureProviderMembers(ctx context.Context, provider string) ([]modelv1.ResourceMemberAccess, []uint64, error) {
	// 获取指定提供者的订阅
	subscriptions, err := p.store.CloudResource().ListByProvider(ctx, provider)
	if err != nil {
		log.L(ctx).Errorf("failed to list %s subscriptions: %s", provider, err.Error())
		return nil, nil, err
	}

	if len(subscriptions) == 0 {
		log.L(ctx).Infof("no subscriptions found for provider: %s", provider)
		return []modelv1.ResourceMemberAccess{}, []uint64{}, nil
	}

	// 获取数据库中的用户
	users, err := p.store.User().Gets(ctx)
	if err != nil {
		log.L(ctx).Errorf("failed to get users: %s", err.Error())
		return nil, nil, err
	}

	// 获取指定提供者的角色
	roles, err := p.store.CloudMemberRole().ListByPro(ctx, provider)
	if err != nil {
		log.L(ctx).Errorf("failed to get %s roles: %s", provider, err.Error())
		return nil, nil, err
	}

	// 构建用户映射表 - 使用Azure Object ID进行匹配
	uMap := make(map[string]*modelv1.User)
	rMap := make(map[string]*modelv1.CloudMemberRole)
	rIDs := make([]uint64, 0, len(roles))

	for _, u := range users {
		// 使用Azure Object ID进行映射
		if u.AzureObjectID != "" {
			uMap[u.AzureObjectID] = u
		}
	}
	for _, r := range roles {
		rMap[r.NameInProvider] = r
		rIDs = append(rIDs, r.ID)
	}

	// 获取Azure凭据
	cred, err := p.getAzureCredential(ctx)
	if err != nil {
		log.L(ctx).Error("failed to get azure credentials:" + err.Error())
		return nil, nil, err
	}

	// 并发处理订阅
	ch := make(chan int, 8)
	wg := sync.WaitGroup{}
	membersChan := make(chan []modelv1.ResourceMemberAccess, len(subscriptions))

	for _, subscription := range subscriptions {
		wg.Add(1)
		ch <- 1
		go func(sub *modelv1.CloudResource) {
			defer func() {
				<-ch
				wg.Done()
			}()

			// 为这个特定订阅创建授权客户端
			authClient, err := armauthorization.NewRoleAssignmentsClient(sub.CloudInsID, cred, nil)
			if err != nil {
				log.L(ctx).Errorf("failed to create azure auth client for subscription %s: %v", sub.CloudInsID, err)
				membersChan <- []modelv1.ResourceMemberAccess{}
				return
			}

			// 获取此订阅的角色分配
			subMembers := p.syncAzureSubscriptionRoleAssignments(ctx, authClient, sub, roles, uMap, rMap)
			membersChan <- subMembers
		}(subscription)
	}

	// 收集所有结果
	go func() {
		wg.Wait()
		close(membersChan)
	}()

	var members []modelv1.ResourceMemberAccess
	for subMembers := range membersChan {
		members = append(members, subMembers...)
	}

	return members, rIDs, nil
}

// syncAzureSubscriptionRoleAssignments 同步单个Azure订阅的角色分配
func (p *Provider) syncAzureSubscriptionRoleAssignments(ctx context.Context,
	authClient *armauthorization.RoleAssignmentsClient, subscription *modelv1.CloudResource,
	roles []*modelv1.CloudMemberRole, uMap map[string]*modelv1.User, rMap map[string]*modelv1.CloudMemberRole) []modelv1.ResourceMemberAccess {

	var subMembers []modelv1.ResourceMemberAccess

	// 构建订阅范围
	subscriptionScope := "/subscriptions/" + subscription.CloudInsID

	// 列出此订阅的角色分配
	pager := authClient.NewListForScopePager(subscriptionScope, nil)

	for pager.More() {
		page, err := pager.NextPage(ctx)
		if err != nil {
			log.L(ctx).Warnf("failed to get role assignments for subscription %s: %v", subscription.Name, err)
			break
		}

		for _, assignment := range page.Value {
			if assignment == nil || assignment.Properties == nil {
				continue
			}

			props := assignment.Properties
			if props.RoleDefinitionID == nil || props.PrincipalID == nil {
				continue
			}

			// 从角色定义ID中提取角色名称
			roleDefParts := strings.Split(*props.RoleDefinitionID, "/")
			if len(roleDefParts) == 0 {
				continue
			}
			roleDefID := roleDefParts[len(roleDefParts)-1]

			// 在我们的角色映射中查找角色，通过与NameInProvider比较
			var matchedRole *modelv1.CloudMemberRole
			for _, role := range roles {
				if strings.Contains(role.NameInProvider, roleDefID) || role.NameInProvider == *props.RoleDefinitionID {
					matchedRole = role
					break
				}
			}

			if matchedRole == nil {
				continue // 角色不在我们允许的角色中
			}

			// 获取主体ID - 现在我们可以直接使用它与AzureObjectID匹配
			principalID := *props.PrincipalID

			// 使用AzureObjectID在用户映射中查找用户
			if user, exists := uMap[principalID]; exists && user.LarkUserID != "" {
				subMembers = append(subMembers, modelv1.ResourceMemberAccess{
					CreatedAt:    time.Now(),
					MemberLarkID: user.LarkUserID,
					ResourceID:   subscription.ID,
					RoleID:       matchedRole.ID,
				})
			}
		}
	}

	return subMembers
}

// getAzureUsers 使用Microsoft Graph REST API检索Azure AD用户
func (p *Provider) getAzureUsers(ctx context.Context, cred azcore.TokenCredential) ([]assume_role.AzureUser, error) {
	// 从viper获取Azure配置
	clientID := p.config.ClientID
	clientSecret := p.config.ClientSecret
	tenantID := p.config.TenantID

	if clientID == "" || clientSecret == "" || tenantID == "" {
		log.L(ctx).Error("Azure credentials not configured properly")
		return nil, fmt.Errorf("Azure credentials not configured")
	}

	// 使用与resource_manager_worker相同的方法获取访问令牌
	accessToken, err := p.getAzureAccessToken(ctx, tenantID, clientID, clientSecret)
	if err != nil {
		log.L(ctx).Errorf("failed to get access token: %v", err)
		return nil, err
	}

	var allUsers []assume_role.AzureUser
	nextLink := "https://graph.microsoft.com/v1.0/users?$select=id,mail,userPrincipalName,displayName,accountEnabled&$top=999"

	// 分页获取所有用户
	for nextLink != "" {
		users, next, err := p.fetchUsersPage(ctx, nextLink, accessToken)
		if err != nil {
			log.L(ctx).Errorf("failed to fetch users page: %v", err)
			return nil, err
		}

		// 将Graph用户转换为我们的AzureUser格式
		for _, user := range users {
			azureUser := p.convertGraphUserToAzureUser(user)
			if azureUser != nil {
				allUsers = append(allUsers, *azureUser)
			}
		}

		nextLink = next
	}

	log.L(ctx).Infof("successfully retrieved %d Azure AD users", len(allUsers))
	return allUsers, nil
}

// getAzureAccessToken 获取Azure访问令牌（与resource_manager_worker中相同）
func (p *Provider) 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 assume_role.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
}

// fetchUsersPage 从Microsoft Graph API获取单页用户
func (p *Provider) fetchUsersPage(ctx context.Context, url, accessToken string) ([]assume_role.GraphUser, string, error) {
	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, "", fmt.Errorf("failed to create 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 nil, "", fmt.Errorf("failed to make request: %w", err)
	}
	defer resp.Body.Close()

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

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, "", fmt.Errorf("failed to read response body: %w", err)
	}

	var response assume_role.GraphUsersResponse
	if err := json.Unmarshal(body, &response); err != nil {
		return nil, "", fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return response.Value, response.ODataNextLink, nil
}

// convertGraphUserToAzureUser 将Graph API用户转换为我们的AzureUser结构
func (p *Provider) convertGraphUserToAzureUser(user assume_role.GraphUser) *assume_role.AzureUser {
	// 跳过禁用的账户
	if !user.AccountEnabled {
		return nil
	}

	// Object ID是必需的
	if user.ID == "" {
		return nil
	}

	azureUser := &assume_role.AzureUser{
		ObjectId:          user.ID,
		UserPrincipalName: user.UserPrincipalName,
	}

	// 如果可用，使用mail，否则回退到userPrincipalName
	if user.Mail != "" {
		azureUser.Email = user.Mail
	} else if user.UserPrincipalName != "" {
		azureUser.Email = user.UserPrincipalName
	} else {
		// 跳过没有邮箱的用户
		return nil
	}

	return azureUser
}

// deduplicateMembers 删除重复的用户|角色|资源组合
func (p *Provider) deduplicateMembers(members []modelv1.ResourceMemberAccess) []modelv1.ResourceMemberAccess {
	uniqueMap := make(map[string]modelv1.ResourceMemberAccess)

	for _, member := range members {
		// 构建唯一键：member_lark_id|role_id|resource_id
		uniqueKey := member.MemberLarkID + "|" + strconv.FormatUint(member.RoleID, 10) + "|" + strconv.FormatUint(member.ResourceID, 10)

		// 如果已存在相同的组合，保留最新的记录（后面的覆盖前面的）
		uniqueMap[uniqueKey] = member
	}

	// 转换回切片
	var uniqueMembers []modelv1.ResourceMemberAccess
	for _, member := range uniqueMap {
		uniqueMembers = append(uniqueMembers, member)
	}

	return uniqueMembers
}

// diff 比较成员差异
func (p *Provider) diff(membersInDB, membersInCd []modelv1.ResourceMemberAccess) ([]modelv1.ResourceMemberAccess, []modelv1.ResourceMemberAccess) {
	var add []modelv1.ResourceMemberAccess
	var del []modelv1.ResourceMemberAccess
	dbMap := make(map[string]modelv1.ResourceMemberAccess)
	for _, m := range membersInDB {
		uniqueCode := m.MemberLarkID + strconv.FormatUint(m.RoleID, 10) + strconv.FormatUint(m.ResourceID, 10)
		dbMap[uniqueCode] = m
	}
	for _, m := range membersInCd {
		uniqueCode := m.MemberLarkID + strconv.FormatUint(m.RoleID, 10) + strconv.FormatUint(m.ResourceID, 10)
		if _, ok := dbMap[uniqueCode]; !ok {
			m.TimeTag = time.Unix(0, 0).Unix()
			add = append(add, m)
		} else {
			dbMap[uniqueCode] = modelv1.ResourceMemberAccess{
				RequestNote: "exist",
			}
		}
	}
	for _, m := range membersInDB {
		uniqueCode := m.MemberLarkID + strconv.FormatUint(m.RoleID, 10) + strconv.FormatUint(m.ResourceID, 10)
		if dbMap[uniqueCode].RequestNote != "exist" {
			m.TimeTag = time.Now().Unix()
			del = append(del, m)
		}
	}

	return add, del
}
