package dingtalk

import (
	"context"
	"fmt"
	"time"

	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	dingtalkcard_1_0 "github.com/alibabacloud-go/dingtalk/card_1_0"
	dingtalkim_1_0 "github.com/alibabacloud-go/dingtalk/im_1_0"
	dingtalkoauth2_1_0 "github.com/alibabacloud-go/dingtalk/oauth2_1_0"
	"github.com/alibabacloud-go/tea/tea"
)

// Client 钉钉官方 SDK 客户端（按机器人凭证一份实例）
type Client struct {
	ClientID     string
	ClientSecret string
	cardClient   *dingtalkcard_1_0.Client
	imClient     *dingtalkim_1_0.Client
	oauthClient  *dingtalkoauth2_1_0.Client
}

// NewDingTalkClient 创建钉钉官方客户端
func NewDingTalkClient(clientID, clientSecret string) *Client {
	config := &openapi.Config{
		Protocol: tea.String("https"),
		RegionId: tea.String("central"),
	}
	cardClient, _ := dingtalkcard_1_0.NewClient(config)
	imClient, _ := dingtalkim_1_0.NewClient(config)
	oauthClient, _ := dingtalkoauth2_1_0.NewClient(config)

	return &Client{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		imClient:     imClient,
		oauthClient:  oauthClient,
		cardClient:   cardClient,
	}
}

// GetAccessToken 获取 access_token（带 L1 内存 + L2 可插拔缓存 + singleflight）
func (c *Client) GetAccessToken() (string, error) {
	return c.GetAccessTokenCtx(context.Background())
}

// GetAccessTokenCtx 带 ctx 的 token 获取
func (c *Client) GetAccessTokenCtx(ctx context.Context) (string, error) {
	return globalTokenManager.Fetch(ctx, c.ClientID, c.refreshAccessToken)
}

// InvalidateAccessToken 主动失效 token 缓存（收到授权类错误时调用）
func (c *Client) InvalidateAccessToken(ctx context.Context) {
	globalTokenManager.Invalidate(ctx, c.ClientID)
}

// refreshAccessToken 真正调用钉钉接口获取 token，内部使用
func (c *Client) refreshAccessToken(_ context.Context) (string, time.Duration, error) {
	req := &dingtalkoauth2_1_0.GetAccessTokenRequest{
		AppKey:    tea.String(c.ClientID),
		AppSecret: tea.String(c.ClientSecret),
	}
	resp, err := c.oauthClient.GetAccessToken(req)
	if err != nil {
		return "", 0, err
	}
	if resp == nil || resp.Body == nil {
		return "", 0, fmt.Errorf("GetAccessToken 响应为空")
	}
	token := tea.StringValue(resp.Body.AccessToken)
	if token == "" {
		return "", 0, fmt.Errorf("GetAccessToken 返回空 token")
	}
	ttl := time.Duration(tea.Int64Value(resp.Body.ExpireIn)) * time.Second
	if ttl <= 0 {
		ttl = tokenMaxTTL
	}
	return token, ttl, nil
}
