package service

import (
	"context"
	"log"
	"strconv"
	"time"
)

// TokenRefresher 定义平台特定的token刷新策略接口
// 通过此接口可以扩展支持不同平台（Anthropic/OpenAI/Gemini）
type TokenRefresher interface {
	// CanRefresh 检查此刷新器是否能处理指定账号
	CanRefresh(account *Account) bool

	// NeedsRefresh 检查账号的token是否需要刷新
	NeedsRefresh(account *Account, refreshWindow time.Duration) bool

	// Refresh 执行token刷新，返回更新后的credentials
	// 注意：返回的map应该保留原有credentials中的所有字段，只更新token相关字段
	Refresh(ctx context.Context, account *Account) (map[string]any, error)
}

// ClaudeTokenRefresher 处理Anthropic/Claude OAuth token刷新
type ClaudeTokenRefresher struct {
	oauthService *OAuthService
}

// NewClaudeTokenRefresher 创建Claude token刷新器
func NewClaudeTokenRefresher(oauthService *OAuthService) *ClaudeTokenRefresher {
	return &ClaudeTokenRefresher{
		oauthService: oauthService,
	}
}

// CanRefresh 检查是否能处理此账号
// 只处理 anthropic 平台的 oauth 类型账号
// setup-token 虽然也是OAuth，但有效期1年，不需要频繁刷新
func (r *ClaudeTokenRefresher) CanRefresh(account *Account) bool {
	return account.Platform == PlatformAnthropic &&
		account.Type == AccountTypeOAuth
}

// NeedsRefresh 检查token是否需要刷新
// 基于 expires_at 字段判断是否在刷新窗口内
func (r *ClaudeTokenRefresher) NeedsRefresh(account *Account, refreshWindow time.Duration) bool {
	expiresAt := account.GetCredentialAsTime("expires_at")
	if expiresAt == nil {
		return false
	}
	return time.Until(*expiresAt) < refreshWindow
}

// Refresh 执行token刷新
// 保留原有credentials中的所有字段，只更新token相关字段
func (r *ClaudeTokenRefresher) Refresh(ctx context.Context, account *Account) (map[string]any, error) {
	tokenInfo, err := r.oauthService.RefreshAccountToken(ctx, account)
	if err != nil {
		return nil, err
	}

	// 保留现有credentials中的所有字段
	newCredentials := make(map[string]any)
	for k, v := range account.Credentials {
		newCredentials[k] = v
	}

	// 只更新token相关字段
	// 注意：expires_at 和 expires_in 必须存为字符串，因为 GetCredential 只返回 string 类型
	newCredentials["access_token"] = tokenInfo.AccessToken
	newCredentials["token_type"] = tokenInfo.TokenType
	newCredentials["expires_in"] = strconv.FormatInt(tokenInfo.ExpiresIn, 10)
	newCredentials["expires_at"] = strconv.FormatInt(tokenInfo.ExpiresAt, 10)
	if tokenInfo.RefreshToken != "" {
		newCredentials["refresh_token"] = tokenInfo.RefreshToken
	}
	if tokenInfo.Scope != "" {
		newCredentials["scope"] = tokenInfo.Scope
	}

	return newCredentials, nil
}

// OpenAITokenRefresher 处理 OpenAI OAuth token刷新
type OpenAITokenRefresher struct {
	openaiOAuthService *OpenAIOAuthService
	accountRepo        AccountRepository
	soraAccountRepo    SoraAccountRepository // Sora 扩展表仓储，用于双表同步
}

// NewOpenAITokenRefresher 创建 OpenAI token刷新器
func NewOpenAITokenRefresher(openaiOAuthService *OpenAIOAuthService, accountRepo AccountRepository) *OpenAITokenRefresher {
	return &OpenAITokenRefresher{
		openaiOAuthService: openaiOAuthService,
		accountRepo:        accountRepo,
	}
}

// SetSoraAccountRepo 设置 Sora 账号扩展表仓储
// 用于在 Token 刷新时同步更新 sora_accounts 表
// 如果未设置，syncLinkedSoraAccounts 只会更新 accounts.credentials
func (r *OpenAITokenRefresher) SetSoraAccountRepo(repo SoraAccountRepository) {
	r.soraAccountRepo = repo
}

// CanRefresh 检查是否能处理此账号
// 只处理 openai 平台的 oauth 类型账号
func (r *OpenAITokenRefresher) CanRefresh(account *Account) bool {
	return (account.Platform == PlatformOpenAI || account.Platform == PlatformSora) &&
		account.Type == AccountTypeOAuth
}

// NeedsRefresh 检查token是否需要刷新
// 基于 expires_at 字段判断是否在刷新窗口内
func (r *OpenAITokenRefresher) NeedsRefresh(account *Account, refreshWindow time.Duration) bool {
	expiresAt := account.GetCredentialAsTime("expires_at")
	if expiresAt == nil {
		return false
	}

	return time.Until(*expiresAt) < refreshWindow
}

// Refresh 执行token刷新
// 保留原有credentials中的所有字段，只更新token相关字段
// 刷新成功后，异步同步关联的 Sora 账号
func (r *OpenAITokenRefresher) Refresh(ctx context.Context, account *Account) (map[string]any, error) {
	tokenInfo, err := r.openaiOAuthService.RefreshAccountToken(ctx, account)
	if err != nil {
		return nil, err
	}

	// 使用服务提供的方法构建新凭证，并保留原有字段
	newCredentials := r.openaiOAuthService.BuildAccountCredentials(tokenInfo)

	// 保留原有credentials中非token相关字段
	for k, v := range account.Credentials {
		if _, exists := newCredentials[k]; !exists {
			newCredentials[k] = v
		}
	}

	// 异步同步关联的 Sora 账号（不阻塞主流程）
	if r.accountRepo != nil {
		go r.syncLinkedSoraAccounts(context.Background(), account.ID, newCredentials)
	}

	return newCredentials, nil
}

// syncLinkedSoraAccounts 同步关联的 Sora 账号的 token（双表同步）
// 该方法异步执行，失败只记录日志，不影响主流程
//
// 同步策略：
//  1. 更新 accounts.credentials（主表）
//  2. 更新 sora_accounts 扩展表（如果 soraAccountRepo 已设置）
//
// 超时控制：30 秒，防止数据库阻塞导致 goroutine 泄漏
func (r *OpenAITokenRefresher) syncLinkedSoraAccounts(ctx context.Context, openaiAccountID int64, newCredentials map[string]any) {
	// 添加超时控制，防止 goroutine 泄漏
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	// 1. 查找所有关联的 Sora 账号（限定 platform='sora'）
	soraAccounts, err := r.accountRepo.FindByExtraField(ctx, "linked_openai_account_id", openaiAccountID)
	if err != nil {
		log.Printf("[TokenSync] 查找关联 Sora 账号失败: openai_account_id=%d err=%v", openaiAccountID, err)
		return
	}

	if len(soraAccounts) == 0 {
		// 没有关联的 Sora 账号，直接返回
		return
	}

	// 2. 同步更新每个 Sora 账号的双表数据
	for _, soraAccount := range soraAccounts {
		// 2.1 更新 accounts.credentials（主表）
		soraAccount.Credentials["access_token"] = newCredentials["access_token"]
		soraAccount.Credentials["refresh_token"] = newCredentials["refresh_token"]
		if expiresAt, ok := newCredentials["expires_at"]; ok {
			soraAccount.Credentials["expires_at"] = expiresAt
		}

		if err := r.accountRepo.Update(ctx, &soraAccount); err != nil {
			log.Printf("[TokenSync] 更新 Sora accounts 表失败: sora_account_id=%d openai_account_id=%d err=%v",
				soraAccount.ID, openaiAccountID, err)
			continue
		}

		// 2.2 更新 sora_accounts 扩展表（如果仓储已设置）
		if r.soraAccountRepo != nil {
			soraUpdates := map[string]any{
				"access_token":  newCredentials["access_token"],
				"refresh_token": newCredentials["refresh_token"],
			}
			if err := r.soraAccountRepo.Upsert(ctx, soraAccount.ID, soraUpdates); err != nil {
				log.Printf("[TokenSync] 更新 sora_accounts 表失败: account_id=%d openai_account_id=%d err=%v",
					soraAccount.ID, openaiAccountID, err)
				// 继续处理其他账号，不中断
			}
		}

		log.Printf("[TokenSync] 成功同步 Sora 账号 token: sora_account_id=%d openai_account_id=%d dual_table=%v",
			soraAccount.ID, openaiAccountID, r.soraAccountRepo != nil)
	}
}
