package efile

import (
	"fmt"
	"net/url"
	"sync"
	"time"

	"gitlink.org.cn/cloudream/common/utils/http2"
	"gitlink.org.cn/cloudream/common/utils/serder"
	clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/factory/reg"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/utils"
	cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
)

func init() {
	reg.RegisterBuilder[*cortypes.EFileType](func(detail *clitypes.UserSpaceDetail) types.StorageBuilder {
		return &builder{
			detail: detail,
		}
	})
}

type builder struct {
	types.EmptyBuilder
	detail       *clitypes.UserSpaceDetail
	token        string
	tokenLock    sync.Mutex
	getTokenTime time.Time
}

func (b *builder) getToken() (string, error) {
	cred := b.detail.UserSpace.Credential.(*cortypes.EFileCred)

	b.tokenLock.Lock()
	defer b.tokenLock.Unlock()

	if b.token != "" {
		dt := time.Since(b.getTokenTime)
		if dt < time.Second*time.Duration(cred.TokenExpire) {
			return b.token, nil
		}
	}

	u, err := url.JoinPath(cred.TokenURL, "/ac/openapi/v2/tokens")
	if err != nil {
		return "", err
	}

	resp, err := http2.PostJSON(u, http2.RequestParam{
		Header: map[string]string{
			"user":     cred.User,
			"password": cred.Password,
			"orgId":    cred.OrgID,
		},
	})
	if err != nil {
		return "", err
	}

	type Response struct {
		Code string `json:"code"`
		Msg  string `json:"msg"`
		Data []struct {
			ClusterID string `json:"clusterId"`
			Token     string `json:"token"`
		} `json:"data"`
	}

	var r Response
	err = serder.JSONToObjectStream(resp.Body, &r)
	if err != nil {
		return "", err
	}

	if r.Code != "0" {
		return "", fmt.Errorf("code:%s, msg:%s", r.Code, r.Msg)
	}

	for _, d := range r.Data {
		if d.ClusterID == cred.ClusterID {
			b.token = d.Token
			b.getTokenTime = time.Now()
			return d.Token, nil
		}
	}

	return "", fmt.Errorf("clusterID %s not found", cred.ClusterID)
}

func (b *builder) CreateECMultiplier() (types.ECMultiplier, error) {
	feat := utils.FindFeature[*cortypes.ECMultiplierFeature](b.detail.Storage)
	if feat == nil {
		return nil, fmt.Errorf("feature ECMultiplier not found")
	}

	cred, ok := b.detail.UserSpace.Credential.(*cortypes.EFileCred)
	if !ok {
		return nil, fmt.Errorf("invalid storage credential type %T for efile storage", b.detail.UserSpace.Credential)
	}

	return &ECMultiplier{
		blder: b,
		url:   cred.APIURL,
		feat:  feat,
	}, nil
}
