package cost

import (
	"context"
	"fmt"
	"sync"

	"github.com/pingcap/log"
	"github.com/tidbcloud/cost-collect/pkg/global"
)

type Context struct {
	ctx     context.Context
	wg      *sync.WaitGroup
	request global.Request
	items   []global.Item
	err     error
}

type ProviderClientInterface interface {
	GetProviderCost(context.Context, global.Request) ([]global.Item, error)
}

type Manager struct {
	plugins map[string]ProviderClientInterface
}

func (t *Manager) Register(key string, plugin ProviderClientInterface) error {
	_, ok := t.plugins[key]
	if ok {
		return fmt.Errorf("conflict:%s is exist", key)
	}
	t.plugins[key] = plugin
	return nil
}

func (t Manager) QueryBill(ctx context.Context, request global.Request) ([]global.Item, error) {

	kv := map[string]*Context{}
	wg := &sync.WaitGroup{}

	f := func(invoke ProviderClientInterface, ptr *Context) {
		log.S().Debugf("start")

		defer func() {
			if ptr.err != nil {
				log.S().Errorf("invoke error:", ptr.err)
			}
			log.S().Debugf("done")
			ptr.wg.Done()
		}()

		response, err := invoke.GetProviderCost(ptr.ctx, ptr.request)
		if err != nil {
			ptr.err = err
			return
		}
		ptr.items = response

	}

	for k, v := range t.plugins {
		log.S().Debugf("%s,%s,%s,%s,plugin:%s",
			request.TenantID, request.ProjectID, request.StartTime, request.EndTime, k)

		ptr := &Context{request: request, ctx: ctx, wg: wg}
		kv[k] = ptr
		wg.Add(1)
		go f(v, ptr)
	}
	wg.Wait()
	items := []global.Item{}
	for k, v := range kv {
		if v.err != nil {
			return nil, fmt.Errorf("%s:%s", k, v.err)
		} else {
			items = append(items, v.items...)
		}
	}

	return items, nil
}

func NewManager() *Manager {
	m := Manager{
		plugins: map[string]ProviderClientInterface{},
	}
	return &m
}
