package enricher

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	tntent "github.com/tidbcloud/devops/entity/tenant"
	"github.com/tidbcloud/devops/pkg/infra/clients/accountcli"
	"github.com/tidbcloud/devops/pkg/infra/clients/googlecli"
	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"

	"cloud.google.com/go/bigquery"
	"google.golang.org/api/iterator"
)

type biTenant struct {
	TenantId           string              `bigquery:"tenant_id"`
	TenantAlias        bigquery.NullString `bigquery:"tenant_alias"`
	TenantRegion       bigquery.NullString `bigquery:"tenant_region"`
	TenantCustomerId   bigquery.NullString `bigquery:"tenant_customer_id"`
	TenantTAMName      bigquery.NullString `bigquery:"tenant_tam_name"`
	TenantPlan         bigquery.NullString `bigquery:"tenant_plan"`
	SalesOwnerEmail    bigquery.NullString `bigquery:"sales_owner_email"`
	TenantSegmentation bigquery.NullString `bigquery:"tenant_segmentation"`
	TenantInternalUser bool                `bigquery:"tenant_internal_user"`
}

type SalesforceEnricher struct {
	bigQueryCli             googlecli.BigQueryCli
	bigQueryTenantTableName string
	accountCli              accountcli.Client
	testInfraTenantIDs      container.Set[uint64]
}

func NewSalesforceEnricher(
	bigQueryCli googlecli.BigQueryCli,
	accountCli accountcli.Client,
	bigQueryTenantTableName string,
	testInfraTenantIDs []uint64,
) *SalesforceEnricher {
	return &SalesforceEnricher{
		bigQueryCli:             bigQueryCli,
		accountCli:              accountCli,
		bigQueryTenantTableName: bigQueryTenantTableName,
		testInfraTenantIDs:      container.NewSetFromSlice(testInfraTenantIDs),
	}
}

func (e *SalesforceEnricher) Enrich(ctx context.Context, origins []*tntent.Tenant) error {
	biTenantMap, err := e.listTenantFromBigQuery(ctx)
	if err != nil {
		return err
	}

	for _, origin := range origins {
		biTenant, ok := biTenantMap[strconv.FormatUint(origin.Id, 10)]
		if !ok {
			e.overrideTestInfraRegion(origin)
			continue
		}
		if biTenant.TenantAlias.Valid {
			origin.Alias = biTenant.TenantAlias.String()
		}
		if biTenant.TenantCustomerId.Valid {
			origin.CustomID = biTenant.TenantCustomerId.String()
		}
		if biTenant.TenantSegmentation.Valid {
			origin.Segmentation = tntent.TenantSegmentation(biTenant.TenantSegmentation.String())
		}
		if biTenant.TenantRegion.Valid {
			origin.SalesForceRegion = tntent.TenantRegion(strings.ToUpper(biTenant.TenantRegion.String()))
		}
		e.overrideTestInfraRegion(origin)
		if biTenant.SalesOwnerEmail.Valid {
			origin.SalesOwnerEmail = biTenant.SalesOwnerEmail.String()
		}
		if biTenant.TenantInternalUser {
			origin.SalesForceKind = "internal"
		} else {
			origin.SalesForceKind = "external"
		}
		if biTenant.TenantTAMName.Valid {
			origin.TAMName = biTenant.TenantTAMName.String()
		}

		// update tenant kind to account
		if origin.SalesForceKind != "" && origin.Kind != origin.SalesForceKind {
			go func(tenant *tntent.Tenant) {
				log.FromContext(ctx).Info("update tenant kind to account",
					"tenant_id", tenant.Id,
					"account_kind", origin.Kind,
					"sales_force_kind", tenant.SalesForceKind,
				)
				orgKind := account.OrgKind_ORG_KIND_INTERNAL
				if tenant.SalesForceKind == tntent.TenantKindExternal {
					orgKind = account.OrgKind_ORG_KIND_EXTERNAL
				}
				err := e.accountCli.UpdateTenantKind(context.Background(), tenant.Id, orgKind)
				if err != nil {
					log.FromContext(ctx).Error(err, "update tenant kind to account failed",
						"tenant_id", tenant.Id,
						"account_kind", origin.Kind,
						"sales_force_kind", tenant.SalesForceKind,
					)
				}
			}(origin)
		}
	}

	return nil
}

func (e *SalesforceEnricher) overrideTestInfraRegion(tenant *tntent.Tenant) {
	if e.testInfraTenantIDs.Has(tenant.Id) {
		// Configured override: ignore the Salesforce-sourced region for test-infra tenants.
		tenant.SalesForceRegion = tntent.TenantRegionTestInfra
	}
}

func (e *SalesforceEnricher) listTenantFromBigQuery(ctx context.Context) (map[string]biTenant, error) {
	biTenants, err := e.bigQueryCli.Query(ctx,
		fmt.Sprintf(`
		SELECT 
			tenant_id, 
			account_alias AS tenant_alias,
			tenant_region, 
			account_id AS tenant_customer_id, 
			tenant_plan,
			account_owner_email AS sales_owner_email, 
			account_segmentation AS tenant_segmentation,
            account_tam_name AS tenant_tam_name,
			tenant_internal_user
		FROM %s
		`, e.bigQueryTenantTableName),
	)
	if err != nil {
		return nil, fmt.Errorf("list tenants from bigquery failed: %s", err)
	}
	biTenantMap := make(map[string]biTenant)
	for {
		var v biTenant
		err := biTenants.Next(&v)
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("get tenant from bigquery failed: %s", err)
		}
		biTenantMap[v.TenantId] = v
	}
	return biTenantMap, nil
}
