package gcp

import (
	"context"
	"fmt"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/spf13/viper"
	"google.golang.org/api/cloudresourcemanager/v1"
	"google.golang.org/api/option"

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

const (
	UserPrefix = "user:"
)

// Provider GCP云平台提供者
type Provider struct {
	config *Config
	store  store.Factory
}

// Config GCP配置
type Config struct {
	ServiceAccountFile string `mapstructure:"service_account_file"`
}

// NewProvider 创建GCP提供者
func NewProvider() (*Provider, error) {
	return NewProviderWithStore(nil)
}

// NewProviderWithStore 创建带有store的GCP提供者
func NewProviderWithStore(storeFactory store.Factory) (*Provider, error) {
	config := &Config{}

	// 从配置中读取GCP凭据
	if err := viper.UnmarshalKey("gcp", config); err != nil {
		return nil, fmt.Errorf("failed to unmarshal GCP config: %w", err)
	}

	// 设置默认值
	if config.ServiceAccountFile == "" {
		config.ServiceAccountFile = viper.GetString("rm.sa")
	}

	// 验证配置
	if err := config.Validate(); err != nil {
		return nil, fmt.Errorf("invalid GCP config: %w", err)
	}

	return &Provider{
		config: config,
		store:  storeFactory,
	}, nil
}

// ValidateConfig 验证配置
func (c *Config) Validate() error {
	if c.ServiceAccountFile == "" {
		return errors.New("GCP service_account_file is required")
	}

	// 检查服务账户文件是否存在
	if _, err := os.Stat(c.ServiceAccountFile); err != nil {
		return fmt.Errorf("service account file not found: %w", err)
	}

	return nil
}

// GetProviderName 获取平台名称
func (p *Provider) GetProviderName() string {
	return "GCP"
}

// ValidateConfig 验证配置
func (p *Provider) ValidateConfig() error {
	return p.config.Validate()
}

// SyncUserRoles 同步用户角色
func (p *Provider) SyncUserRoles(ctx context.Context, roles []*modelv1.RoleMembers, cloudResource *modelv1.CloudResource, optionType int) error {
	projectID := cloudResource.CloudInsID
	log.L(ctx).Infof("Starting GCP role sync for project: %s", projectID)

	// 验证角色
	if err := p.validateRoles(roles); err != nil {
		return err
	}

	// 获取Google客户端
	client, err := p.getGoogleClient(ctx)
	if err != nil {
		return fmt.Errorf("failed to get Google client: %w", err)
	}

	switch optionType {
	case modelv1.Add:
		return p.addUserRoles(ctx, roles, cloudResource, client)
	case modelv1.Delete:
		return p.deleteUserRoles(ctx, roles, cloudResource, client)
	case modelv1.DeleteAll:
		return p.deleteAllUserRoles(ctx, roles, cloudResource, client)
	default:
		return errors.New("unknown option type")
	}
}

// validateRoles 验证角色
func (p *Provider) validateRoles(roles []*modelv1.RoleMembers) error {
	if len(roles) == 0 {
		return errors.New("role is empty")
	}

	for _, role := range roles {
		if role.Role == nil {
			continue
		}
		if _, ok := modelv1.GCPAllowedRoles[role.Role.NameInProvider]; !ok {
			return fmt.Errorf("role %s is not allowed in GCP", role.Role.NameInProvider)
		}
	}
	return nil
}

// addUserRoles 添加用户角色
func (p *Provider) addUserRoles(ctx context.Context, roles []*modelv1.RoleMembers, cloudResource *modelv1.CloudResource, client *cloudresourcemanager.Service) error {
	// 获取当前IAM策略
	policy, err := client.Projects.GetIamPolicy(cloudResource.CloudInsID, &cloudresourcemanager.GetIamPolicyRequest{}).Do()
	if err != nil {
		return fmt.Errorf("failed to get IAM policy: %w", err)
	}

	// 添加新的绑定
	for _, role := range roles {
		var emails []string
		for _, member := range role.Members {
			emails = append(emails, UserPrefix+member.Email)
		}

		newBinding := &cloudresourcemanager.Binding{
			Role:    role.Role.NameInProvider,
			Members: emails,
		}

		policy.Bindings = append(policy.Bindings, newBinding)
	}

	// 设置新的IAM策略
	_, err = client.Projects.SetIamPolicy(cloudResource.CloudInsID, &cloudresourcemanager.SetIamPolicyRequest{
		Policy: policy,
	}).Do()
	if err != nil {
		return fmt.Errorf("failed to set IAM policy: %w", err)
	}

	log.L(ctx).Infof("Successfully added roles to GCP project %s", cloudResource.CloudInsID)
	return nil
}

// deleteUserRoles 删除用户角色
func (p *Provider) deleteUserRoles(ctx context.Context, roles []*modelv1.RoleMembers, cloudResource *modelv1.CloudResource, client *cloudresourcemanager.Service) error {
	// 获取当前IAM策略
	policy, err := client.Projects.GetIamPolicy(cloudResource.CloudInsID, &cloudresourcemanager.GetIamPolicyRequest{}).Do()
	if err != nil {
		return fmt.Errorf("failed to get IAM policy: %w", err)
	}

	// 构建角色映射
	rMap := make(map[string]*modelv1.RoleMembers)
	for _, role := range roles {
		rMap[role.Role.NameInProvider] = role
	}

	// 从绑定中删除指定的成员
	for _, binding := range policy.Bindings {
		if role, ok := rMap[binding.Role]; ok {
			var newMembers []string
			uMap := make(map[string]bool)

			// 构建要删除的成员映射
			for _, member := range role.Members {
				uMap[UserPrefix+member.Email] = true
			}

			// 保留不在删除列表中的成员
			for _, member := range binding.Members {
				if _, in := uMap[member]; !in {
					newMembers = append(newMembers, member)
				}
			}

			binding.Members = newMembers
		}
	}

	// 设置新的IAM策略
	_, err = client.Projects.SetIamPolicy(cloudResource.CloudInsID, &cloudresourcemanager.SetIamPolicyRequest{
		Policy: policy,
	}).Do()
	if err != nil {
		return fmt.Errorf("failed to set IAM policy: %w", err)
	}

	log.L(ctx).Infof("Successfully deleted roles from GCP project %s", cloudResource.CloudInsID)
	return nil
}

// deleteAllUserRoles 删除所有用户角色
func (p *Provider) deleteAllUserRoles(ctx context.Context, roles []*modelv1.RoleMembers, cloudResource *modelv1.CloudResource, client *cloudresourcemanager.Service) error {
	// 获取当前IAM策略
	policy, err := client.Projects.GetIamPolicy(cloudResource.CloudInsID, &cloudresourcemanager.GetIamPolicyRequest{}).Do()
	if err != nil {
		return fmt.Errorf("failed to get IAM policy: %w", err)
	}

	// 构建要删除的成员映射
	uMap := make(map[string]bool)
	for _, role := range roles {
		for _, member := range role.Members {
			uMap[UserPrefix+member.Email] = true
		}
	}

	// 从所有绑定中删除指定的成员
	for _, binding := range policy.Bindings {
		var newMembers []string
		for _, member := range binding.Members {
			if _, in := uMap[member]; !in {
				newMembers = append(newMembers, member)
			}
		}
		binding.Members = newMembers
	}

	// 设置新的IAM策略
	_, err = client.Projects.SetIamPolicy(cloudResource.CloudInsID, &cloudresourcemanager.SetIamPolicyRequest{
		Policy: policy,
	}).Do()
	if err != nil {
		return fmt.Errorf("failed to set IAM policy: %w", err)
	}

	log.L(ctx).Infof("Successfully deleted all specified roles from GCP project %s", cloudResource.CloudInsID)
	return nil
}

// getGoogleClient 获取Google客户端
func (p *Provider) getGoogleClient(ctx context.Context) (*cloudresourcemanager.Service, error) {
	// 检查服务账户文件
	f, err := os.Stat(p.config.ServiceAccountFile)
	if err != nil || f.IsDir() {
		return nil, fmt.Errorf("service account file not found or is directory: %w", err)
	}

	// 创建IAM服务客户端
	iamSrv, err := cloudresourcemanager.NewService(ctx, option.WithCredentialsFile(p.config.ServiceAccountFile))
	if err != nil {
		return nil, fmt.Errorf("failed to create IAM service: %w", err)
	}

	return iamSrv, nil
}

// GetProviderMembers 获取提供者成员信息
func (p *Provider) GetProviderMembers(ctx context.Context, provider string) ([]modelv1.ResourceMemberAccess, []uint64, error) {
	if p.store == nil {
		return nil, nil, errors.New("store not initialized")
	}

	projs, err := p.store.CloudResource().ListByProvider(ctx, "GCP")
	if err != nil {
		return nil, nil, err
	}
	users, err := p.store.User().Gets(ctx)
	if err != nil {
		return nil, nil, err
	}
	roles, err := p.store.CloudMemberRole().ListByPro(ctx, "GCP")
	if err != nil {
		return nil, nil, err
	}

	uMap := make(map[string]*modelv1.User)
	rMap := make(map[string]*modelv1.CloudMemberRole)
	rIDs := make([]uint64, 0)
	for _, u := range users {
		uMap[u.Email] = u
	}
	for _, r := range roles {
		rMap[r.NameInProvider] = r
		rIDs = append(rIDs, r.ID)
	}

	req := &cloudresourcemanager.GetIamPolicyRequest{}
	googleSrv, err := p.getGoogleClient(ctx)
	if err != nil {
		return nil, nil, err
	}

	ch := make(chan int, 8)
	wg := sync.WaitGroup{}
	var members []modelv1.ResourceMemberAccess

	for _, proj := range projs {
		if proj.ClassType == "Prod" {
			continue
		}
		wg.Add(1)
		ch <- 1
		go func(project *modelv1.CloudResource) {
			defer func() {
				<-ch
				wg.Done()
			}()
			do, err := googleSrv.Projects.GetIamPolicy(project.CloudInsID, req).Do()
			if err != nil {
				log.L(ctx).Error(project.ManagementID + "||get iam policy failed:" + err.Error())
				return
			}

			for _, binding := range do.Bindings {
				if !strings.HasPrefix(binding.Role, "roles") {
					continue
				}
				if _, ok := rMap[binding.Role]; !ok {
					continue
				}
				for _, member := range binding.Members {
					if !strings.Contains(member, UserPrefix) || strings.Contains(member, "deleted") {
						continue
					}
					uName := strings.ToLower(strings.ReplaceAll(member, UserPrefix, ""))
					if _, ok := uMap[uName]; !ok {
						continue
					}
					if uMap[uName].LarkUserID == "" {
						continue
					}
					members = append(members, modelv1.ResourceMemberAccess{
						CreatedAt:    time.Now(),
						MemberLarkID: uMap[uName].LarkUserID,
						ResourceID:   project.ID,
						RoleID:       rMap[binding.Role].ID,
					})
				}
			}
		}(proj)
	}
	wg.Wait()

	return members, rIDs, nil
}

// SyncUsers 同步用户到数据库 (GCP不需要单独的用户同步)
func (p *Provider) SyncUsers(ctx context.Context) error {
	log.L(ctx).Info("GCP does not require separate user sync")
	return nil
}

// SyncMembers 同步成员信息到数据库
func (p *Provider) SyncMembers(ctx context.Context) error {
	if p.store == nil {
		return errors.New("store not initialized")
	}

	members, rIDs, err := p.GetProviderMembers(ctx, "GCP")
	if err != nil {
		return err
	}

	membersInDB, err := p.store.ResourceMemberAccess().ListByRIDs(ctx, rIDs)
	if err != nil {
		log.L(ctx).Error("list member access failed:" + err.Error())
		return err
	}

	add, del := p.diff(membersInDB, members)
	err = p.store.ResourceMemberAccess().SyncToDB(ctx, add, del)
	if err != nil {
		log.L(ctx).Error("sync gcp member to db failed:" + err.Error())
		return err
	}

	log.L(ctx).Info("sync gcp member to db success")
	return nil
}

// GetGoogleClient 获取Google客户端（公开方法）
func (p *Provider) GetGoogleClient(ctx context.Context) (*cloudresourcemanager.Service, error) {
	return p.getGoogleClient(ctx)
}

// 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
}
