package repository

import (
	"context"
	"time"

	"github.com/Wei-Shaw/sub2api/internal/service"

	"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
	"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
	"github.com/Wei-Shaw/sub2api/internal/pkg/usagestats"

	"gorm.io/gorm"
)

type usageLogRepository struct {
	db *gorm.DB
}

func NewUsageLogRepository(db *gorm.DB) service.UsageLogRepository {
	return &usageLogRepository{db: db}
}

// getPerformanceStats 获取 RPM 和 TPM（近5分钟平均值，可选按用户过滤）
func (r *usageLogRepository) getPerformanceStats(ctx context.Context, userID int64) (rpm, tpm int64) {
	fiveMinutesAgo := time.Now().Add(-5 * time.Minute)
	var perfStats struct {
		RequestCount int64 `gorm:"column:request_count"`
		TokenCount   int64 `gorm:"column:token_count"`
	}

	db := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as request_count,
			COALESCE(SUM(input_tokens + output_tokens), 0) as token_count
		`).
		Where("created_at >= ?", fiveMinutesAgo)

	if userID > 0 {
		db = db.Where("user_id = ?", userID)
	}

	db.Scan(&perfStats)
	// 返回5分钟平均值
	return perfStats.RequestCount / 5, perfStats.TokenCount / 5
}

func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog) error {
	m := usageLogModelFromService(log)
	err := r.db.WithContext(ctx).Create(m).Error
	if err == nil {
		applyUsageLogModelToService(log, m)
	}
	return err
}

func (r *usageLogRepository) GetByID(ctx context.Context, id int64) (*service.UsageLog, error) {
	var log usageLogModel
	err := r.db.WithContext(ctx).First(&log, id).Error
	if err != nil {
		return nil, translatePersistenceError(err, service.ErrUsageLogNotFound, nil)
	}
	return usageLogModelToService(&log), nil
}

func (r *usageLogRepository) ListByUser(ctx context.Context, userID int64, params pagination.PaginationParams) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	var total int64

	db := r.db.WithContext(ctx).Model(&usageLogModel{}).Where("user_id = ?", userID)

	if err := db.Count(&total).Error; err != nil {
		return nil, nil, err
	}

	if err := db.Offset(params.Offset()).Limit(params.Limit()).Order("id DESC").Find(&logs).Error; err != nil {
		return nil, nil, err
	}

	return usageLogModelsToService(logs), paginationResultFromTotal(total, params), nil
}

func (r *usageLogRepository) ListByApiKey(ctx context.Context, apiKeyID int64, params pagination.PaginationParams) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	var total int64

	db := r.db.WithContext(ctx).Model(&usageLogModel{}).Where("api_key_id = ?", apiKeyID)

	if err := db.Count(&total).Error; err != nil {
		return nil, nil, err
	}

	if err := db.Offset(params.Offset()).Limit(params.Limit()).Order("id DESC").Find(&logs).Error; err != nil {
		return nil, nil, err
	}

	return usageLogModelsToService(logs), paginationResultFromTotal(total, params), nil
}

// UserStats 用户使用统计
type UserStats struct {
	TotalRequests   int64   `json:"total_requests"`
	TotalTokens     int64   `json:"total_tokens"`
	TotalCost       float64 `json:"total_cost"`
	InputTokens     int64   `json:"input_tokens"`
	OutputTokens    int64   `json:"output_tokens"`
	CacheReadTokens int64   `json:"cache_read_tokens"`
}

func (r *usageLogRepository) GetUserStats(ctx context.Context, userID int64, startTime, endTime time.Time) (*UserStats, error) {
	var stats UserStats
	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as total_requests,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as total_tokens,
			COALESCE(SUM(actual_cost), 0) as total_cost,
			COALESCE(SUM(input_tokens), 0) as input_tokens,
			COALESCE(SUM(output_tokens), 0) as output_tokens,
			COALESCE(SUM(cache_read_tokens), 0) as cache_read_tokens
		`).
		Where("user_id = ? AND created_at >= ? AND created_at < ?", userID, startTime, endTime).
		Scan(&stats).Error
	return &stats, err
}

// DashboardStats 仪表盘统计
type DashboardStats = usagestats.DashboardStats

func (r *usageLogRepository) GetDashboardStats(ctx context.Context) (*DashboardStats, error) {
	var stats DashboardStats
	today := timezone.Today()
	now := time.Now()

	// 合并用户统计查询
	var userStats struct {
		TotalUsers     int64 `gorm:"column:total_users"`
		TodayNewUsers  int64 `gorm:"column:today_new_users"`
		ActiveUsers    int64 `gorm:"column:active_users"`
	}
	if err := r.db.WithContext(ctx).Raw(`
		SELECT
			COUNT(*) as total_users,
			COUNT(CASE WHEN created_at >= ? THEN 1 END) as today_new_users,
			(SELECT COUNT(DISTINCT user_id) FROM usage_logs WHERE created_at >= ?) as active_users
		FROM users
	`, today, today).Scan(&userStats).Error; err != nil {
		return nil, err
	}
	stats.TotalUsers = userStats.TotalUsers
	stats.TodayNewUsers = userStats.TodayNewUsers
	stats.ActiveUsers = userStats.ActiveUsers

	// 合并API Key统计查询
	var apiKeyStats struct {
		TotalApiKeys  int64 `gorm:"column:total_api_keys"`
		ActiveApiKeys int64 `gorm:"column:active_api_keys"`
	}
	if err := r.db.WithContext(ctx).Raw(`
		SELECT
			COUNT(*) as total_api_keys,
			COUNT(CASE WHEN status = ? THEN 1 END) as active_api_keys
		FROM api_keys
	`, service.StatusActive).Scan(&apiKeyStats).Error; err != nil {
		return nil, err
	}
	stats.TotalApiKeys = apiKeyStats.TotalApiKeys
	stats.ActiveApiKeys = apiKeyStats.ActiveApiKeys

	// 合并账户统计查询
	var accountStats struct {
		TotalAccounts      int64 `gorm:"column:total_accounts"`
		NormalAccounts     int64 `gorm:"column:normal_accounts"`
		ErrorAccounts      int64 `gorm:"column:error_accounts"`
		RateLimitAccounts  int64 `gorm:"column:ratelimit_accounts"`
		OverloadAccounts   int64 `gorm:"column:overload_accounts"`
	}
	if err := r.db.WithContext(ctx).Raw(`
		SELECT
			COUNT(*) as total_accounts,
			COUNT(CASE WHEN status = ? AND schedulable = true THEN 1 END) as normal_accounts,
			COUNT(CASE WHEN status = ? THEN 1 END) as error_accounts,
			COUNT(CASE WHEN rate_limited_at IS NOT NULL AND rate_limit_reset_at > ? THEN 1 END) as ratelimit_accounts,
			COUNT(CASE WHEN overload_until IS NOT NULL AND overload_until > ? THEN 1 END) as overload_accounts
		FROM accounts
	`, service.StatusActive, service.StatusError, now, now).Scan(&accountStats).Error; err != nil {
		return nil, err
	}
	stats.TotalAccounts = accountStats.TotalAccounts
	stats.NormalAccounts = accountStats.NormalAccounts
	stats.ErrorAccounts = accountStats.ErrorAccounts
	stats.RateLimitAccounts = accountStats.RateLimitAccounts
	stats.OverloadAccounts = accountStats.OverloadAccounts

	// 累计 Token 统计
	var totalStats struct {
		TotalRequests            int64   `gorm:"column:total_requests"`
		TotalInputTokens         int64   `gorm:"column:total_input_tokens"`
		TotalOutputTokens        int64   `gorm:"column:total_output_tokens"`
		TotalCacheCreationTokens int64   `gorm:"column:total_cache_creation_tokens"`
		TotalCacheReadTokens     int64   `gorm:"column:total_cache_read_tokens"`
		TotalCost                float64 `gorm:"column:total_cost"`
		TotalActualCost          float64 `gorm:"column:total_actual_cost"`
		AverageDurationMs        float64 `gorm:"column:avg_duration_ms"`
	}
	r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as total_requests,
			COALESCE(SUM(input_tokens), 0) as total_input_tokens,
			COALESCE(SUM(output_tokens), 0) as total_output_tokens,
			COALESCE(SUM(cache_creation_tokens), 0) as total_cache_creation_tokens,
			COALESCE(SUM(cache_read_tokens), 0) as total_cache_read_tokens,
			COALESCE(SUM(total_cost), 0) as total_cost,
			COALESCE(SUM(actual_cost), 0) as total_actual_cost,
			COALESCE(AVG(duration_ms), 0) as avg_duration_ms
		`).
		Scan(&totalStats)

	stats.TotalRequests = totalStats.TotalRequests
	stats.TotalInputTokens = totalStats.TotalInputTokens
	stats.TotalOutputTokens = totalStats.TotalOutputTokens
	stats.TotalCacheCreationTokens = totalStats.TotalCacheCreationTokens
	stats.TotalCacheReadTokens = totalStats.TotalCacheReadTokens
	stats.TotalTokens = stats.TotalInputTokens + stats.TotalOutputTokens + stats.TotalCacheCreationTokens + stats.TotalCacheReadTokens
	stats.TotalCost = totalStats.TotalCost
	stats.TotalActualCost = totalStats.TotalActualCost
	stats.AverageDurationMs = totalStats.AverageDurationMs

	// 今日 Token 统计
	var todayStats struct {
		TodayRequests            int64   `gorm:"column:today_requests"`
		TodayInputTokens         int64   `gorm:"column:today_input_tokens"`
		TodayOutputTokens        int64   `gorm:"column:today_output_tokens"`
		TodayCacheCreationTokens int64   `gorm:"column:today_cache_creation_tokens"`
		TodayCacheReadTokens     int64   `gorm:"column:today_cache_read_tokens"`
		TodayCost                float64 `gorm:"column:today_cost"`
		TodayActualCost          float64 `gorm:"column:today_actual_cost"`
	}
	r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as today_requests,
			COALESCE(SUM(input_tokens), 0) as today_input_tokens,
			COALESCE(SUM(output_tokens), 0) as today_output_tokens,
			COALESCE(SUM(cache_creation_tokens), 0) as today_cache_creation_tokens,
			COALESCE(SUM(cache_read_tokens), 0) as today_cache_read_tokens,
			COALESCE(SUM(total_cost), 0) as today_cost,
			COALESCE(SUM(actual_cost), 0) as today_actual_cost
		`).
		Where("created_at >= ?", today).
		Scan(&todayStats)

	stats.TodayRequests = todayStats.TodayRequests
	stats.TodayInputTokens = todayStats.TodayInputTokens
	stats.TodayOutputTokens = todayStats.TodayOutputTokens
	stats.TodayCacheCreationTokens = todayStats.TodayCacheCreationTokens
	stats.TodayCacheReadTokens = todayStats.TodayCacheReadTokens
	stats.TodayTokens = stats.TodayInputTokens + stats.TodayOutputTokens + stats.TodayCacheCreationTokens + stats.TodayCacheReadTokens
	stats.TodayCost = todayStats.TodayCost
	stats.TodayActualCost = todayStats.TodayActualCost

	// 性能指标：RPM 和 TPM（最近1分钟，全局）
	stats.Rpm, stats.Tpm = r.getPerformanceStats(ctx, 0)

	return &stats, nil
}

func (r *usageLogRepository) ListByAccount(ctx context.Context, accountID int64, params pagination.PaginationParams) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	var total int64

	db := r.db.WithContext(ctx).Model(&usageLogModel{}).Where("account_id = ?", accountID)

	if err := db.Count(&total).Error; err != nil {
		return nil, nil, err
	}

	if err := db.Offset(params.Offset()).Limit(params.Limit()).Order("id DESC").Find(&logs).Error; err != nil {
		return nil, nil, err
	}

	return usageLogModelsToService(logs), paginationResultFromTotal(total, params), nil
}

func (r *usageLogRepository) ListByUserAndTimeRange(ctx context.Context, userID int64, startTime, endTime time.Time) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	err := r.db.WithContext(ctx).
		Where("user_id = ? AND created_at >= ? AND created_at < ?", userID, startTime, endTime).
		Order("id DESC").
		Find(&logs).Error
	return usageLogModelsToService(logs), nil, err
}

// GetUserStatsAggregated returns aggregated usage statistics for a user using database-level aggregation
func (r *usageLogRepository) GetUserStatsAggregated(ctx context.Context, userID int64, startTime, endTime time.Time) (*usagestats.UsageStats, error) {
	var stats struct {
		TotalRequests     int64   `gorm:"column:total_requests"`
		TotalInputTokens  int64   `gorm:"column:total_input_tokens"`
		TotalOutputTokens int64   `gorm:"column:total_output_tokens"`
		TotalCacheTokens  int64   `gorm:"column:total_cache_tokens"`
		TotalCost         float64 `gorm:"column:total_cost"`
		TotalActualCost   float64 `gorm:"column:total_actual_cost"`
		AverageDurationMs float64 `gorm:"column:avg_duration_ms"`
	}

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as total_requests,
			COALESCE(SUM(input_tokens), 0) as total_input_tokens,
			COALESCE(SUM(output_tokens), 0) as total_output_tokens,
			COALESCE(SUM(cache_creation_tokens + cache_read_tokens), 0) as total_cache_tokens,
			COALESCE(SUM(total_cost), 0) as total_cost,
			COALESCE(SUM(actual_cost), 0) as total_actual_cost,
			COALESCE(AVG(COALESCE(duration_ms, 0)), 0) as avg_duration_ms
		`).
		Where("user_id = ? AND created_at >= ? AND created_at < ?", userID, startTime, endTime).
		Scan(&stats).Error

	if err != nil {
		return nil, err
	}

	return &usagestats.UsageStats{
		TotalRequests:     stats.TotalRequests,
		TotalInputTokens:  stats.TotalInputTokens,
		TotalOutputTokens: stats.TotalOutputTokens,
		TotalCacheTokens:  stats.TotalCacheTokens,
		TotalTokens:       stats.TotalInputTokens + stats.TotalOutputTokens + stats.TotalCacheTokens,
		TotalCost:         stats.TotalCost,
		TotalActualCost:   stats.TotalActualCost,
		AverageDurationMs: stats.AverageDurationMs,
	}, nil
}

// GetApiKeyStatsAggregated returns aggregated usage statistics for an API key using database-level aggregation
func (r *usageLogRepository) GetApiKeyStatsAggregated(ctx context.Context, apiKeyID int64, startTime, endTime time.Time) (*usagestats.UsageStats, error) {
	var stats struct {
		TotalRequests     int64   `gorm:"column:total_requests"`
		TotalInputTokens  int64   `gorm:"column:total_input_tokens"`
		TotalOutputTokens int64   `gorm:"column:total_output_tokens"`
		TotalCacheTokens  int64   `gorm:"column:total_cache_tokens"`
		TotalCost         float64 `gorm:"column:total_cost"`
		TotalActualCost   float64 `gorm:"column:total_actual_cost"`
		AverageDurationMs float64 `gorm:"column:avg_duration_ms"`
	}

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as total_requests,
			COALESCE(SUM(input_tokens), 0) as total_input_tokens,
			COALESCE(SUM(output_tokens), 0) as total_output_tokens,
			COALESCE(SUM(cache_creation_tokens + cache_read_tokens), 0) as total_cache_tokens,
			COALESCE(SUM(total_cost), 0) as total_cost,
			COALESCE(SUM(actual_cost), 0) as total_actual_cost,
			COALESCE(AVG(COALESCE(duration_ms, 0)), 0) as avg_duration_ms
		`).
		Where("api_key_id = ? AND created_at >= ? AND created_at < ?", apiKeyID, startTime, endTime).
		Scan(&stats).Error

	if err != nil {
		return nil, err
	}

	return &usagestats.UsageStats{
		TotalRequests:     stats.TotalRequests,
		TotalInputTokens:  stats.TotalInputTokens,
		TotalOutputTokens: stats.TotalOutputTokens,
		TotalCacheTokens:  stats.TotalCacheTokens,
		TotalTokens:       stats.TotalInputTokens + stats.TotalOutputTokens + stats.TotalCacheTokens,
		TotalCost:         stats.TotalCost,
		TotalActualCost:   stats.TotalActualCost,
		AverageDurationMs: stats.AverageDurationMs,
	}, nil
}

func (r *usageLogRepository) ListByApiKeyAndTimeRange(ctx context.Context, apiKeyID int64, startTime, endTime time.Time) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	err := r.db.WithContext(ctx).
		Where("api_key_id = ? AND created_at >= ? AND created_at < ?", apiKeyID, startTime, endTime).
		Order("id DESC").
		Find(&logs).Error
	return usageLogModelsToService(logs), nil, err
}

func (r *usageLogRepository) ListByAccountAndTimeRange(ctx context.Context, accountID int64, startTime, endTime time.Time) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	err := r.db.WithContext(ctx).
		Where("account_id = ? AND created_at >= ? AND created_at < ?", accountID, startTime, endTime).
		Order("id DESC").
		Find(&logs).Error
	return usageLogModelsToService(logs), nil, err
}

func (r *usageLogRepository) ListByModelAndTimeRange(ctx context.Context, modelName string, startTime, endTime time.Time) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	err := r.db.WithContext(ctx).
		Where("model = ? AND created_at >= ? AND created_at < ?", modelName, startTime, endTime).
		Order("id DESC").
		Find(&logs).Error
	return usageLogModelsToService(logs), nil, err
}

func (r *usageLogRepository) Delete(ctx context.Context, id int64) error {
	return r.db.WithContext(ctx).Delete(&usageLogModel{}, id).Error
}

// GetAccountTodayStats 获取账号今日统计
func (r *usageLogRepository) GetAccountTodayStats(ctx context.Context, accountID int64) (*usagestats.AccountStats, error) {
	today := timezone.Today()

	var stats struct {
		Requests int64   `gorm:"column:requests"`
		Tokens   int64   `gorm:"column:tokens"`
		Cost     float64 `gorm:"column:cost"`
	}

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as requests,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
			COALESCE(SUM(actual_cost), 0) as cost
		`).
		Where("account_id = ? AND created_at >= ?", accountID, today).
		Scan(&stats).Error

	if err != nil {
		return nil, err
	}

	return &usagestats.AccountStats{
		Requests: stats.Requests,
		Tokens:   stats.Tokens,
		Cost:     stats.Cost,
	}, nil
}

// GetAccountWindowStats 获取账号时间窗口内的统计
func (r *usageLogRepository) GetAccountWindowStats(ctx context.Context, accountID int64, startTime time.Time) (*usagestats.AccountStats, error) {
	var stats struct {
		Requests int64   `gorm:"column:requests"`
		Tokens   int64   `gorm:"column:tokens"`
		Cost     float64 `gorm:"column:cost"`
	}

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as requests,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
			COALESCE(SUM(actual_cost), 0) as cost
		`).
		Where("account_id = ? AND created_at >= ?", accountID, startTime).
		Scan(&stats).Error

	if err != nil {
		return nil, err
	}

	return &usagestats.AccountStats{
		Requests: stats.Requests,
		Tokens:   stats.Tokens,
		Cost:     stats.Cost,
	}, nil
}

// TrendDataPoint represents a single point in trend data
type TrendDataPoint = usagestats.TrendDataPoint

// ModelStat represents usage statistics for a single model
type ModelStat = usagestats.ModelStat

// UserUsageTrendPoint represents user usage trend data point
type UserUsageTrendPoint = usagestats.UserUsageTrendPoint

// ApiKeyUsageTrendPoint represents API key usage trend data point
type ApiKeyUsageTrendPoint = usagestats.ApiKeyUsageTrendPoint

// GetApiKeyUsageTrend returns usage trend data grouped by API key and date
func (r *usageLogRepository) GetApiKeyUsageTrend(ctx context.Context, startTime, endTime time.Time, granularity string, limit int) ([]ApiKeyUsageTrendPoint, error) {
	var results []ApiKeyUsageTrendPoint

	// Choose date format based on granularity
	var dateFormat string
	if granularity == "hour" {
		dateFormat = "YYYY-MM-DD HH24:00"
	} else {
		dateFormat = "YYYY-MM-DD"
	}

	// Use raw SQL for complex subquery
	query := `
		WITH top_keys AS (
			SELECT api_key_id
			FROM usage_logs
			WHERE created_at >= ? AND created_at < ?
			GROUP BY api_key_id
			ORDER BY SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens) DESC
			LIMIT ?
		)
		SELECT
			TO_CHAR(u.created_at, '` + dateFormat + `') as date,
			u.api_key_id,
			COALESCE(k.name, '') as key_name,
			COUNT(*) as requests,
			COALESCE(SUM(u.input_tokens + u.output_tokens + u.cache_creation_tokens + u.cache_read_tokens), 0) as tokens
		FROM usage_logs u
		LEFT JOIN api_keys k ON u.api_key_id = k.id
		WHERE u.api_key_id IN (SELECT api_key_id FROM top_keys)
		  AND u.created_at >= ? AND u.created_at < ?
		GROUP BY date, u.api_key_id, k.name
		ORDER BY date ASC, tokens DESC
	`

	err := r.db.WithContext(ctx).Raw(query, startTime, endTime, limit, startTime, endTime).Scan(&results).Error
	if err != nil {
		return nil, err
	}

	return results, nil
}

// GetUserUsageTrend returns usage trend data grouped by user and date
func (r *usageLogRepository) GetUserUsageTrend(ctx context.Context, startTime, endTime time.Time, granularity string, limit int) ([]UserUsageTrendPoint, error) {
	var results []UserUsageTrendPoint

	// Choose date format based on granularity
	var dateFormat string
	if granularity == "hour" {
		dateFormat = "YYYY-MM-DD HH24:00"
	} else {
		dateFormat = "YYYY-MM-DD"
	}

	// Use raw SQL for complex subquery
	query := `
		WITH top_users AS (
			SELECT user_id
			FROM usage_logs
			WHERE created_at >= ? AND created_at < ?
			GROUP BY user_id
			ORDER BY SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens) DESC
			LIMIT ?
		)
		SELECT
			TO_CHAR(u.created_at, '` + dateFormat + `') as date,
			u.user_id,
			COALESCE(us.email, '') as email,
			COUNT(*) as requests,
			COALESCE(SUM(u.input_tokens + u.output_tokens + u.cache_creation_tokens + u.cache_read_tokens), 0) as tokens,
			COALESCE(SUM(u.total_cost), 0) as cost,
			COALESCE(SUM(u.actual_cost), 0) as actual_cost
		FROM usage_logs u
		LEFT JOIN users us ON u.user_id = us.id
		WHERE u.user_id IN (SELECT user_id FROM top_users)
		  AND u.created_at >= ? AND u.created_at < ?
		GROUP BY date, u.user_id, us.email
		ORDER BY date ASC, tokens DESC
	`

	err := r.db.WithContext(ctx).Raw(query, startTime, endTime, limit, startTime, endTime).Scan(&results).Error
	if err != nil {
		return nil, err
	}

	return results, nil
}

// UserDashboardStats 用户仪表盘统计
type UserDashboardStats = usagestats.UserDashboardStats

// GetUserDashboardStats 获取用户专属的仪表盘统计
func (r *usageLogRepository) GetUserDashboardStats(ctx context.Context, userID int64) (*UserDashboardStats, error) {
	var stats UserDashboardStats
	today := timezone.Today()

	// API Key 统计
	r.db.WithContext(ctx).Model(&apiKeyModel{}).
		Where("user_id = ?", userID).
		Count(&stats.TotalApiKeys)

	r.db.WithContext(ctx).Model(&apiKeyModel{}).
		Where("user_id = ? AND status = ?", userID, service.StatusActive).
		Count(&stats.ActiveApiKeys)

	// 累计 Token 统计
	var totalStats struct {
		TotalRequests            int64   `gorm:"column:total_requests"`
		TotalInputTokens         int64   `gorm:"column:total_input_tokens"`
		TotalOutputTokens        int64   `gorm:"column:total_output_tokens"`
		TotalCacheCreationTokens int64   `gorm:"column:total_cache_creation_tokens"`
		TotalCacheReadTokens     int64   `gorm:"column:total_cache_read_tokens"`
		TotalCost                float64 `gorm:"column:total_cost"`
		TotalActualCost          float64 `gorm:"column:total_actual_cost"`
		AverageDurationMs        float64 `gorm:"column:avg_duration_ms"`
	}
	r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as total_requests,
			COALESCE(SUM(input_tokens), 0) as total_input_tokens,
			COALESCE(SUM(output_tokens), 0) as total_output_tokens,
			COALESCE(SUM(cache_creation_tokens), 0) as total_cache_creation_tokens,
			COALESCE(SUM(cache_read_tokens), 0) as total_cache_read_tokens,
			COALESCE(SUM(total_cost), 0) as total_cost,
			COALESCE(SUM(actual_cost), 0) as total_actual_cost,
			COALESCE(AVG(duration_ms), 0) as avg_duration_ms
		`).
		Where("user_id = ?", userID).
		Scan(&totalStats)

	stats.TotalRequests = totalStats.TotalRequests
	stats.TotalInputTokens = totalStats.TotalInputTokens
	stats.TotalOutputTokens = totalStats.TotalOutputTokens
	stats.TotalCacheCreationTokens = totalStats.TotalCacheCreationTokens
	stats.TotalCacheReadTokens = totalStats.TotalCacheReadTokens
	stats.TotalTokens = stats.TotalInputTokens + stats.TotalOutputTokens + stats.TotalCacheCreationTokens + stats.TotalCacheReadTokens
	stats.TotalCost = totalStats.TotalCost
	stats.TotalActualCost = totalStats.TotalActualCost
	stats.AverageDurationMs = totalStats.AverageDurationMs

	// 今日 Token 统计
	var todayStats struct {
		TodayRequests            int64   `gorm:"column:today_requests"`
		TodayInputTokens         int64   `gorm:"column:today_input_tokens"`
		TodayOutputTokens        int64   `gorm:"column:today_output_tokens"`
		TodayCacheCreationTokens int64   `gorm:"column:today_cache_creation_tokens"`
		TodayCacheReadTokens     int64   `gorm:"column:today_cache_read_tokens"`
		TodayCost                float64 `gorm:"column:today_cost"`
		TodayActualCost          float64 `gorm:"column:today_actual_cost"`
	}
	r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as today_requests,
			COALESCE(SUM(input_tokens), 0) as today_input_tokens,
			COALESCE(SUM(output_tokens), 0) as today_output_tokens,
			COALESCE(SUM(cache_creation_tokens), 0) as today_cache_creation_tokens,
			COALESCE(SUM(cache_read_tokens), 0) as today_cache_read_tokens,
			COALESCE(SUM(total_cost), 0) as today_cost,
			COALESCE(SUM(actual_cost), 0) as today_actual_cost
		`).
		Where("user_id = ? AND created_at >= ?", userID, today).
		Scan(&todayStats)

	stats.TodayRequests = todayStats.TodayRequests
	stats.TodayInputTokens = todayStats.TodayInputTokens
	stats.TodayOutputTokens = todayStats.TodayOutputTokens
	stats.TodayCacheCreationTokens = todayStats.TodayCacheCreationTokens
	stats.TodayCacheReadTokens = todayStats.TodayCacheReadTokens
	stats.TodayTokens = stats.TodayInputTokens + stats.TodayOutputTokens + stats.TodayCacheCreationTokens + stats.TodayCacheReadTokens
	stats.TodayCost = todayStats.TodayCost
	stats.TodayActualCost = todayStats.TodayActualCost

	// 性能指标：RPM 和 TPM（最近1分钟，仅统计该用户的请求）
	stats.Rpm, stats.Tpm = r.getPerformanceStats(ctx, userID)

	return &stats, nil
}

// GetUserUsageTrendByUserID 获取指定用户的使用趋势
func (r *usageLogRepository) GetUserUsageTrendByUserID(ctx context.Context, userID int64, startTime, endTime time.Time, granularity string) ([]TrendDataPoint, error) {
	var results []TrendDataPoint

	var dateFormat string
	if granularity == "hour" {
		dateFormat = "YYYY-MM-DD HH24:00"
	} else {
		dateFormat = "YYYY-MM-DD"
	}

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			TO_CHAR(created_at, ?) as date,
			COUNT(*) as requests,
			COALESCE(SUM(input_tokens), 0) as input_tokens,
			COALESCE(SUM(output_tokens), 0) as output_tokens,
			COALESCE(SUM(cache_creation_tokens + cache_read_tokens), 0) as cache_tokens,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as total_tokens,
			COALESCE(SUM(total_cost), 0) as cost,
			COALESCE(SUM(actual_cost), 0) as actual_cost
		`, dateFormat).
		Where("user_id = ? AND created_at >= ? AND created_at < ?", userID, startTime, endTime).
		Group("date").
		Order("date ASC").
		Scan(&results).Error

	if err != nil {
		return nil, err
	}

	return results, nil
}

// GetUserModelStats 获取指定用户的模型统计
func (r *usageLogRepository) GetUserModelStats(ctx context.Context, userID int64, startTime, endTime time.Time) ([]ModelStat, error) {
	var results []ModelStat

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			model,
			COUNT(*) as requests,
			COALESCE(SUM(input_tokens), 0) as input_tokens,
			COALESCE(SUM(output_tokens), 0) as output_tokens,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as total_tokens,
			COALESCE(SUM(total_cost), 0) as cost,
			COALESCE(SUM(actual_cost), 0) as actual_cost
		`).
		Where("user_id = ? AND created_at >= ? AND created_at < ?", userID, startTime, endTime).
		Group("model").
		Order("total_tokens DESC").
		Scan(&results).Error

	if err != nil {
		return nil, err
	}

	return results, nil
}

// UsageLogFilters represents filters for usage log queries
type UsageLogFilters = usagestats.UsageLogFilters

// ListWithFilters lists usage logs with optional filters (for admin)
func (r *usageLogRepository) ListWithFilters(ctx context.Context, params pagination.PaginationParams, filters UsageLogFilters) ([]service.UsageLog, *pagination.PaginationResult, error) {
	var logs []usageLogModel
	var total int64

	db := r.db.WithContext(ctx).Model(&usageLogModel{})

	// Apply filters
	if filters.UserID > 0 {
		db = db.Where("user_id = ?", filters.UserID)
	}
	if filters.ApiKeyID > 0 {
		db = db.Where("api_key_id = ?", filters.ApiKeyID)
	}
	if filters.AccountID > 0 {
		db = db.Where("account_id = ?", filters.AccountID)
	}
	if filters.GroupID > 0 {
		db = db.Where("group_id = ?", filters.GroupID)
	}
	if filters.Model != "" {
		db = db.Where("model = ?", filters.Model)
	}
	if filters.Stream != nil {
		db = db.Where("stream = ?", *filters.Stream)
	}
	if filters.BillingType != nil {
		db = db.Where("billing_type = ?", *filters.BillingType)
	}
	if filters.StartTime != nil {
		db = db.Where("created_at >= ?", *filters.StartTime)
	}
	if filters.EndTime != nil {
		db = db.Where("created_at <= ?", *filters.EndTime)
	}

	if err := db.Count(&total).Error; err != nil {
		return nil, nil, err
	}

	// Preload user, api_key, account, and group for display
	if err := db.Preload("User").Preload("ApiKey").Preload("Account").Preload("Group").
		Offset(params.Offset()).Limit(params.Limit()).
		Order("id DESC").Find(&logs).Error; err != nil {
		return nil, nil, err
	}

	return usageLogModelsToService(logs), paginationResultFromTotal(total, params), nil
}

// UsageStats represents usage statistics
type UsageStats = usagestats.UsageStats

// BatchUserUsageStats represents usage stats for a single user
type BatchUserUsageStats = usagestats.BatchUserUsageStats

// GetBatchUserUsageStats gets today and total actual_cost for multiple users
func (r *usageLogRepository) GetBatchUserUsageStats(ctx context.Context, userIDs []int64) (map[int64]*BatchUserUsageStats, error) {
	if len(userIDs) == 0 {
		return make(map[int64]*BatchUserUsageStats), nil
	}

	today := timezone.Today()
	result := make(map[int64]*BatchUserUsageStats)

	// Initialize result map
	for _, id := range userIDs {
		result[id] = &BatchUserUsageStats{UserID: id}
	}

	// Get total actual_cost per user
	var totalStats []struct {
		UserID    int64   `gorm:"column:user_id"`
		TotalCost float64 `gorm:"column:total_cost"`
	}
	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select("user_id, COALESCE(SUM(actual_cost), 0) as total_cost").
		Where("user_id IN ?", userIDs).
		Group("user_id").
		Scan(&totalStats).Error
	if err != nil {
		return nil, err
	}

	for _, stat := range totalStats {
		if s, ok := result[stat.UserID]; ok {
			s.TotalActualCost = stat.TotalCost
		}
	}

	// Get today actual_cost per user
	var todayStats []struct {
		UserID    int64   `gorm:"column:user_id"`
		TodayCost float64 `gorm:"column:today_cost"`
	}
	err = r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select("user_id, COALESCE(SUM(actual_cost), 0) as today_cost").
		Where("user_id IN ? AND created_at >= ?", userIDs, today).
		Group("user_id").
		Scan(&todayStats).Error
	if err != nil {
		return nil, err
	}

	for _, stat := range todayStats {
		if s, ok := result[stat.UserID]; ok {
			s.TodayActualCost = stat.TodayCost
		}
	}

	return result, nil
}

// BatchApiKeyUsageStats represents usage stats for a single API key
type BatchApiKeyUsageStats = usagestats.BatchApiKeyUsageStats

// GetBatchApiKeyUsageStats gets today and total actual_cost for multiple API keys
func (r *usageLogRepository) GetBatchApiKeyUsageStats(ctx context.Context, apiKeyIDs []int64) (map[int64]*BatchApiKeyUsageStats, error) {
	if len(apiKeyIDs) == 0 {
		return make(map[int64]*BatchApiKeyUsageStats), nil
	}

	today := timezone.Today()
	result := make(map[int64]*BatchApiKeyUsageStats)

	// Initialize result map
	for _, id := range apiKeyIDs {
		result[id] = &BatchApiKeyUsageStats{ApiKeyID: id}
	}

	// Get total actual_cost per api key
	var totalStats []struct {
		ApiKeyID  int64   `gorm:"column:api_key_id"`
		TotalCost float64 `gorm:"column:total_cost"`
	}
	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select("api_key_id, COALESCE(SUM(actual_cost), 0) as total_cost").
		Where("api_key_id IN ?", apiKeyIDs).
		Group("api_key_id").
		Scan(&totalStats).Error
	if err != nil {
		return nil, err
	}

	for _, stat := range totalStats {
		if s, ok := result[stat.ApiKeyID]; ok {
			s.TotalActualCost = stat.TotalCost
		}
	}

	// Get today actual_cost per api key
	var todayStats []struct {
		ApiKeyID  int64   `gorm:"column:api_key_id"`
		TodayCost float64 `gorm:"column:today_cost"`
	}
	err = r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select("api_key_id, COALESCE(SUM(actual_cost), 0) as today_cost").
		Where("api_key_id IN ? AND created_at >= ?", apiKeyIDs, today).
		Group("api_key_id").
		Scan(&todayStats).Error
	if err != nil {
		return nil, err
	}

	for _, stat := range todayStats {
		if s, ok := result[stat.ApiKeyID]; ok {
			s.TodayActualCost = stat.TodayCost
		}
	}

	return result, nil
}

// GetUsageTrendWithFilters returns usage trend data with optional user/api_key filters
func (r *usageLogRepository) GetUsageTrendWithFilters(ctx context.Context, startTime, endTime time.Time, granularity string, userID, apiKeyID int64) ([]TrendDataPoint, error) {
	var results []TrendDataPoint

	var dateFormat string
	if granularity == "hour" {
		dateFormat = "YYYY-MM-DD HH24:00"
	} else {
		dateFormat = "YYYY-MM-DD"
	}

	db := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			TO_CHAR(created_at, ?) as date,
			COUNT(*) as requests,
			COALESCE(SUM(input_tokens), 0) as input_tokens,
			COALESCE(SUM(output_tokens), 0) as output_tokens,
			COALESCE(SUM(cache_creation_tokens + cache_read_tokens), 0) as cache_tokens,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as total_tokens,
			COALESCE(SUM(total_cost), 0) as cost,
			COALESCE(SUM(actual_cost), 0) as actual_cost
		`, dateFormat).
		Where("created_at >= ? AND created_at < ?", startTime, endTime)

	if userID > 0 {
		db = db.Where("user_id = ?", userID)
	}
	if apiKeyID > 0 {
		db = db.Where("api_key_id = ?", apiKeyID)
	}

	err := db.Group("date").Order("date ASC").Scan(&results).Error
	if err != nil {
		return nil, err
	}

	return results, nil
}

// GetModelStatsWithFilters returns model statistics with optional user/api_key filters
func (r *usageLogRepository) GetModelStatsWithFilters(ctx context.Context, startTime, endTime time.Time, userID, apiKeyID, accountID int64) ([]ModelStat, error) {
	var results []ModelStat

	db := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			model,
			COUNT(*) as requests,
			COALESCE(SUM(input_tokens), 0) as input_tokens,
			COALESCE(SUM(output_tokens), 0) as output_tokens,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as total_tokens,
			COALESCE(SUM(total_cost), 0) as cost,
			COALESCE(SUM(actual_cost), 0) as actual_cost
		`).
		Where("created_at >= ? AND created_at < ?", startTime, endTime)

	if userID > 0 {
		db = db.Where("user_id = ?", userID)
	}
	if apiKeyID > 0 {
		db = db.Where("api_key_id = ?", apiKeyID)
	}
	if accountID > 0 {
		db = db.Where("account_id = ?", accountID)
	}

	err := db.Group("model").Order("total_tokens DESC").Scan(&results).Error
	if err != nil {
		return nil, err
	}

	return results, nil
}

// GetGlobalStats gets usage statistics for all users within a time range
func (r *usageLogRepository) GetGlobalStats(ctx context.Context, startTime, endTime time.Time) (*UsageStats, error) {
	var stats struct {
		TotalRequests     int64   `gorm:"column:total_requests"`
		TotalInputTokens  int64   `gorm:"column:total_input_tokens"`
		TotalOutputTokens int64   `gorm:"column:total_output_tokens"`
		TotalCacheTokens  int64   `gorm:"column:total_cache_tokens"`
		TotalCost         float64 `gorm:"column:total_cost"`
		TotalActualCost   float64 `gorm:"column:total_actual_cost"`
		AverageDurationMs float64 `gorm:"column:avg_duration_ms"`
	}

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			COUNT(*) as total_requests,
			COALESCE(SUM(input_tokens), 0) as total_input_tokens,
			COALESCE(SUM(output_tokens), 0) as total_output_tokens,
			COALESCE(SUM(cache_creation_tokens + cache_read_tokens), 0) as total_cache_tokens,
			COALESCE(SUM(total_cost), 0) as total_cost,
			COALESCE(SUM(actual_cost), 0) as total_actual_cost,
			COALESCE(AVG(duration_ms), 0) as avg_duration_ms
		`).
		Where("created_at >= ? AND created_at <= ?", startTime, endTime).
		Scan(&stats).Error

	if err != nil {
		return nil, err
	}

	return &UsageStats{
		TotalRequests:     stats.TotalRequests,
		TotalInputTokens:  stats.TotalInputTokens,
		TotalOutputTokens: stats.TotalOutputTokens,
		TotalCacheTokens:  stats.TotalCacheTokens,
		TotalTokens:       stats.TotalInputTokens + stats.TotalOutputTokens + stats.TotalCacheTokens,
		TotalCost:         stats.TotalCost,
		TotalActualCost:   stats.TotalActualCost,
		AverageDurationMs: stats.AverageDurationMs,
	}, nil
}

// AccountUsageHistory represents daily usage history for an account
type AccountUsageHistory = usagestats.AccountUsageHistory

// AccountUsageSummary represents summary statistics for an account
type AccountUsageSummary = usagestats.AccountUsageSummary

// AccountUsageStatsResponse represents the full usage statistics response for an account
type AccountUsageStatsResponse = usagestats.AccountUsageStatsResponse

// GetAccountUsageStats returns comprehensive usage statistics for an account over a time range
func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID int64, startTime, endTime time.Time) (*AccountUsageStatsResponse, error) {
	daysCount := int(endTime.Sub(startTime).Hours()/24) + 1
	if daysCount <= 0 {
		daysCount = 30
	}

	// Get daily history
	var historyResults []struct {
		Date       string  `gorm:"column:date"`
		Requests   int64   `gorm:"column:requests"`
		Tokens     int64   `gorm:"column:tokens"`
		Cost       float64 `gorm:"column:cost"`
		ActualCost float64 `gorm:"column:actual_cost"`
	}

	err := r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select(`
			TO_CHAR(created_at, 'YYYY-MM-DD') as date,
			COUNT(*) as requests,
			COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
			COALESCE(SUM(total_cost), 0) as cost,
			COALESCE(SUM(actual_cost), 0) as actual_cost
		`).
		Where("account_id = ? AND created_at >= ? AND created_at < ?", accountID, startTime, endTime).
		Group("date").
		Order("date ASC").
		Scan(&historyResults).Error
	if err != nil {
		return nil, err
	}

	// Build history with labels
	history := make([]AccountUsageHistory, 0, len(historyResults))
	for _, h := range historyResults {
		// Parse date to get label (MM/DD)
		t, _ := time.Parse("2006-01-02", h.Date)
		label := t.Format("01/02")
		history = append(history, AccountUsageHistory{
			Date:       h.Date,
			Label:      label,
			Requests:   h.Requests,
			Tokens:     h.Tokens,
			Cost:       h.Cost,
			ActualCost: h.ActualCost,
		})
	}

	// Calculate summary
	var totalActualCost, totalStandardCost float64
	var totalRequests, totalTokens int64
	var highestCostDay, highestRequestDay *AccountUsageHistory

	for i := range history {
		h := &history[i]
		totalActualCost += h.ActualCost
		totalStandardCost += h.Cost
		totalRequests += h.Requests
		totalTokens += h.Tokens

		if highestCostDay == nil || h.ActualCost > highestCostDay.ActualCost {
			highestCostDay = h
		}
		if highestRequestDay == nil || h.Requests > highestRequestDay.Requests {
			highestRequestDay = h
		}
	}

	actualDaysUsed := len(history)
	if actualDaysUsed == 0 {
		actualDaysUsed = 1
	}

	// Get average duration
	var avgDuration struct {
		AvgDurationMs float64 `gorm:"column:avg_duration_ms"`
	}
	r.db.WithContext(ctx).Model(&usageLogModel{}).
		Select("COALESCE(AVG(duration_ms), 0) as avg_duration_ms").
		Where("account_id = ? AND created_at >= ? AND created_at < ?", accountID, startTime, endTime).
		Scan(&avgDuration)

	summary := AccountUsageSummary{
		Days:              daysCount,
		ActualDaysUsed:    actualDaysUsed,
		TotalCost:         totalActualCost,
		TotalStandardCost: totalStandardCost,
		TotalRequests:     totalRequests,
		TotalTokens:       totalTokens,
		AvgDailyCost:      totalActualCost / float64(actualDaysUsed),
		AvgDailyRequests:  float64(totalRequests) / float64(actualDaysUsed),
		AvgDailyTokens:    float64(totalTokens) / float64(actualDaysUsed),
		AvgDurationMs:     avgDuration.AvgDurationMs,
	}

	// Set today's stats
	todayStr := timezone.Now().Format("2006-01-02")
	for i := range history {
		if history[i].Date == todayStr {
			summary.Today = &struct {
				Date     string  `json:"date"`
				Cost     float64 `json:"cost"`
				Requests int64   `json:"requests"`
				Tokens   int64   `json:"tokens"`
			}{
				Date:     history[i].Date,
				Cost:     history[i].ActualCost,
				Requests: history[i].Requests,
				Tokens:   history[i].Tokens,
			}
			break
		}
	}

	// Set highest cost day
	if highestCostDay != nil {
		summary.HighestCostDay = &struct {
			Date     string  `json:"date"`
			Label    string  `json:"label"`
			Cost     float64 `json:"cost"`
			Requests int64   `json:"requests"`
		}{
			Date:     highestCostDay.Date,
			Label:    highestCostDay.Label,
			Cost:     highestCostDay.ActualCost,
			Requests: highestCostDay.Requests,
		}
	}

	// Set highest request day
	if highestRequestDay != nil {
		summary.HighestRequestDay = &struct {
			Date     string  `json:"date"`
			Label    string  `json:"label"`
			Requests int64   `json:"requests"`
			Cost     float64 `json:"cost"`
		}{
			Date:     highestRequestDay.Date,
			Label:    highestRequestDay.Label,
			Requests: highestRequestDay.Requests,
			Cost:     highestRequestDay.ActualCost,
		}
	}

	// Get model statistics using the unified method
	models, err := r.GetModelStatsWithFilters(ctx, startTime, endTime, 0, 0, accountID)
	if err != nil {
		models = []ModelStat{}
	}

	return &AccountUsageStatsResponse{
		History: history,
		Summary: summary,
		Models:  models,
	}, nil
}

type usageLogModel struct {
	ID        int64  `gorm:"primaryKey"`
	UserID    int64  `gorm:"index;not null"`
	ApiKeyID  int64  `gorm:"index;not null"`
	AccountID int64  `gorm:"index;not null"`
	RequestID string `gorm:"size:64"`
	Model     string `gorm:"size:100;index;not null"`

	GroupID        *int64 `gorm:"index"`
	SubscriptionID *int64 `gorm:"index"`

	InputTokens         int `gorm:"default:0;not null"`
	OutputTokens        int `gorm:"default:0;not null"`
	CacheCreationTokens int `gorm:"default:0;not null"`
	CacheReadTokens     int `gorm:"default:0;not null"`

	CacheCreation5mTokens int `gorm:"default:0;not null"`
	CacheCreation1hTokens int `gorm:"default:0;not null"`

	InputCost         float64 `gorm:"type:decimal(20,10);default:0;not null"`
	OutputCost        float64 `gorm:"type:decimal(20,10);default:0;not null"`
	CacheCreationCost float64 `gorm:"type:decimal(20,10);default:0;not null"`
	CacheReadCost     float64 `gorm:"type:decimal(20,10);default:0;not null"`
	TotalCost         float64 `gorm:"type:decimal(20,10);default:0;not null"`
	ActualCost        float64 `gorm:"type:decimal(20,10);default:0;not null"`
	RateMultiplier    float64 `gorm:"type:decimal(10,4);default:1;not null"`

	BillingType  int8 `gorm:"type:smallint;default:0;not null"`
	Stream       bool `gorm:"default:false;not null"`
	DurationMs   *int
	FirstTokenMs *int

	CreatedAt time.Time `gorm:"index;not null"`

	User         *userModel             `gorm:"foreignKey:UserID"`
	ApiKey       *apiKeyModel           `gorm:"foreignKey:ApiKeyID"`
	Account      *accountModel          `gorm:"foreignKey:AccountID"`
	Group        *groupModel            `gorm:"foreignKey:GroupID"`
	Subscription *userSubscriptionModel `gorm:"foreignKey:SubscriptionID"`
}

func (usageLogModel) TableName() string { return "usage_logs" }

func usageLogModelToService(m *usageLogModel) *service.UsageLog {
	if m == nil {
		return nil
	}
	return &service.UsageLog{
		ID:                    m.ID,
		UserID:                m.UserID,
		ApiKeyID:              m.ApiKeyID,
		AccountID:             m.AccountID,
		RequestID:             m.RequestID,
		Model:                 m.Model,
		GroupID:               m.GroupID,
		SubscriptionID:        m.SubscriptionID,
		InputTokens:           m.InputTokens,
		OutputTokens:          m.OutputTokens,
		CacheCreationTokens:   m.CacheCreationTokens,
		CacheReadTokens:       m.CacheReadTokens,
		CacheCreation5mTokens: m.CacheCreation5mTokens,
		CacheCreation1hTokens: m.CacheCreation1hTokens,
		InputCost:             m.InputCost,
		OutputCost:            m.OutputCost,
		CacheCreationCost:     m.CacheCreationCost,
		CacheReadCost:         m.CacheReadCost,
		TotalCost:             m.TotalCost,
		ActualCost:            m.ActualCost,
		RateMultiplier:        m.RateMultiplier,
		BillingType:           m.BillingType,
		Stream:                m.Stream,
		DurationMs:            m.DurationMs,
		FirstTokenMs:          m.FirstTokenMs,
		CreatedAt:             m.CreatedAt,
		User:                  userModelToService(m.User),
		ApiKey:                apiKeyModelToService(m.ApiKey),
		Account:               accountModelToService(m.Account),
		Group:                 groupModelToService(m.Group),
		Subscription:          userSubscriptionModelToService(m.Subscription),
	}
}

func usageLogModelsToService(models []usageLogModel) []service.UsageLog {
	out := make([]service.UsageLog, 0, len(models))
	for i := range models {
		if s := usageLogModelToService(&models[i]); s != nil {
			out = append(out, *s)
		}
	}
	return out
}

func usageLogModelFromService(log *service.UsageLog) *usageLogModel {
	if log == nil {
		return nil
	}
	return &usageLogModel{
		ID:                    log.ID,
		UserID:                log.UserID,
		ApiKeyID:              log.ApiKeyID,
		AccountID:             log.AccountID,
		RequestID:             log.RequestID,
		Model:                 log.Model,
		GroupID:               log.GroupID,
		SubscriptionID:        log.SubscriptionID,
		InputTokens:           log.InputTokens,
		OutputTokens:          log.OutputTokens,
		CacheCreationTokens:   log.CacheCreationTokens,
		CacheReadTokens:       log.CacheReadTokens,
		CacheCreation5mTokens: log.CacheCreation5mTokens,
		CacheCreation1hTokens: log.CacheCreation1hTokens,
		InputCost:             log.InputCost,
		OutputCost:            log.OutputCost,
		CacheCreationCost:     log.CacheCreationCost,
		CacheReadCost:         log.CacheReadCost,
		TotalCost:             log.TotalCost,
		ActualCost:            log.ActualCost,
		RateMultiplier:        log.RateMultiplier,
		BillingType:           log.BillingType,
		Stream:                log.Stream,
		DurationMs:            log.DurationMs,
		FirstTokenMs:          log.FirstTokenMs,
		CreatedAt:             log.CreatedAt,
	}
}

func applyUsageLogModelToService(log *service.UsageLog, m *usageLogModel) {
	if log == nil || m == nil {
		return
	}
	log.ID = m.ID
	log.CreatedAt = m.CreatedAt
}
