package cron

import (
	"context"
	"fmt"
	"strings"
	"sync"

	tntent "github.com/tidbcloud/devops/entity/tenant"
	"github.com/tidbcloud/devops/pkg/infra/clients/accountcli"
	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	"github.com/tidbcloud/devops/pkg/service/tenant/cron/enricher"
	"github.com/tidbcloud/devops/pkg/service/tenant/region"
	"github.com/tidbcloud/devops/pkg/service/tenant/repo"

	"github.com/tidbcloud/account/idl/pbgen/proto/account"
)

type SyncTenantsCron interface {
	SyncTenants(ctx context.Context) error
}

type syncTenantsCron struct {
	tenantRepo repo.TenantRepo
	accountCli accountcli.Client
	enrichers  []enricher.Enricher

	mutex *sync.Mutex
}

func NewSyncTenantsCron(
	tenantRepo repo.TenantRepo,
	accountCli accountcli.Client,
	enrichers []enricher.Enricher,
) SyncTenantsCron {
	return &syncTenantsCron{
		tenantRepo: tenantRepo,
		mutex:      &sync.Mutex{},
		accountCli: accountCli,
		enrichers:  enrichers,
	}
}

func (c *syncTenantsCron) SyncTenants(ctx context.Context) error {
	ok := c.mutex.TryLock()
	if !ok {
		log.FromContext(ctx).Info("sync tenants is running")
		return nil
	}
	defer c.mutex.Unlock()

	log.FromContext(ctx).Info("start sync tenants")
	if err := c.syncTenants(ctx); err != nil {
		return fmt.Errorf("sync tenants failed: %w", err)
	}
	log.FromContext(ctx).Info("sync tenants done")
	return nil
}

func (c *syncTenantsCron) syncTenants(ctx context.Context) error {
	accountTenants, err := c.accountCli.ListTenants(ctx, &account.OrgFilter{})
	if err != nil {
		return fmt.Errorf("list tenants from account failed: %s", err)
	}
	tenants := make([]*tntent.Tenant, 0, 100)
	for _, accountTenant := range accountTenants {
		tenant := &tntent.Tenant{
			Id:            accountTenant.Id,
			Name:          accountTenant.Name,
			Status:        tntent.TenantStatus(accountTenant.Status),
			Email:         accountTenant.Email,
			CreatedAt:     pointer.Pointer(accountTenant.CreatedAt.AsTime()),
			Country:       accountTenant.Country,
			TimeZone:      accountTenant.TimeZone,
			Kind:          tntent.TenantKind(accountTenant.Kind),
			CountryRegion: region.CountryToRegion(strings.ToUpper(accountTenant.Country)),
		}

		tenants = append(tenants, tenant)
	}

	for _, enricher := range c.enrichers {
		if err := enricher.Enrich(ctx, tenants); err != nil {
			return fmt.Errorf("enrich tenants failed: %w", err)
		}
	}

	for i := 0; i < len(tenants); i += 100 {
		end := i + 100
		if end > len(tenants) {
			end = len(tenants)
		}

		batchTenants := tenants[i:end]
		log.FromContext(ctx).Info("batch save tenants", "count", len(batchTenants))
		if err := c.tenantRepo.BatchSave(ctx, batchTenants); err != nil {
			return fmt.Errorf("batch save tenants failed: %w", err)
		}
	}

	return nil
}
