export type SubscriptionTier = 'free' | 'pro' | 'proplus'; export type SubscriptionStatus = 'none' | 'active' | 'expired' | 'cancelled'; export type NodeStatus = 'done' | 'current' | 'locked' | 'chest'; export type LeaderboardScope = 'region' | 'topic'; export type RewardSource = 'ad' | 'check_in' | 'subscription' | 'admin_grant' | 'debug'; export type SubscriptionPlatform = 'huawei' | 'apple' | 'google'; export interface UserBriefDto { id: string; nickname: string; avatarUrl: string | null; tier: SubscriptionTier; level: number; } export interface SubscriptionDto { status: SubscriptionStatus; tier: SubscriptionTier; expiresAt: string | null; autoRenew?: boolean; } export interface ProgressSummaryDto { hearts: number; maxHearts: number; nextHeartRestoreAt: string | null; dailyAttemptsLeft: number; dailyAttemptsMax: number; nextAttemptResetAt: string | null; highRewardSessionsLeft: number; highRewardSessionsMax: number; xp: number; level: number; xpToNextLevel: number; streakDays: number; checkInDays: number; streakProtectedUntil: string | null; activeTrackId: string | null; isSubscribed: boolean; } export interface ThemeNodeDto { id: string; title: string; status: NodeStatus; reward: string; questionCount: number; } export interface ThemeTrackDto { id: string; name: string; icon: string; progress: number; nodes: ThemeNodeDto[]; } export interface ShopBenefitDto { id: string; type: 'hearts' | 'attempts' | 'streak' | 'subscription'; title: string; description: string; enabled: boolean; requiresAd: boolean; } export interface BootstrapDto { user: UserBriefDto; progress: ProgressSummaryDto; tracks: ThemeTrackDto[]; shopBenefits: readonly ShopBenefitDto[]; subscription: SubscriptionDto; } export interface ChallengeQuestionDto { challengeId: string; trackId: string; nodeId: string; question: { id: string; prompt: string; options: ReadonlyArray<{ id: string; text: string }>; }; } export interface ChallengeSessionDto { challengeId: string; trackId: string; nodeId: string; totalQuestions: number; highRewardEligible: boolean; questions: readonly ChallengeQuestionDto[]; } export interface AnswerRequestDto { challengeId: string; questionId: string; selectedOptionId: string; timeMs: number; submitRequestId?: string; } export interface AnswerResultDto { answerState: 'correct' | 'wrong'; correctOptionId: string; xpDelta: number; progress: { hearts: number; dailyAttemptsLeft: number; highRewardSessionsLeft: number; highRewardSessionsMax: number; xp: number; streakDays: number; }; knowledgeCard: { id: string; title: string; summary: string; fact: string; }; rewards: ReadonlyArray<{ type: string; source?: string; amount?: number; itemId?: string; quantity?: number; title?: string; }>; } export interface LeaderboardEntryDto { rank: number; userId: string; displayName: string; avatarUrl: string | null; xp: number; badge: string; isMe: boolean; }