from fastapi import APIRouter, HTTPException, Query from typing import Optional import uuid from ...models.schemas import UserStatus from ...core.database import db_manager import logging import time router = APIRouter(prefix="/user", tags=["users"]) logger = logging.getLogger("api.users") @router.post("/init", response_model=UserStatus) async def init_user(user_id: Optional[str] = None, referrer_id: Optional[str] = None): async with db_manager.get_connection() as db: if not user_id: user_id = str(uuid.uuid4()) # Check if user exists cursor = await db.execute("SELECT * FROM users WHERE id = ?", (user_id,)) user_row = await cursor.fetchone() if not user_row: # Check user count for first 200 reward cursor = await db.execute("SELECT COUNT(*) FROM users") count = (await cursor.fetchone())[0] welcome_points = 100 is_rewarded = 0 if count < 200: welcome_points = 10000 is_rewarded = 1 logger.info(f"New User {user_id} awarded 10,000 pts (Count: {count})") await db.execute( "INSERT INTO users (id, referrer_id, points, is_rewarded) VALUES (?, ?, ?, ?)", (user_id, referrer_id, welcome_points, is_rewarded) ) if referrer_id: await db.execute( "INSERT OR IGNORE INTO referrals (referrer_id, referred_id, status) VALUES (?, ?, 'joined')", (referrer_id, user_id) ) await db.execute("UPDATE users SET points = points + 50 WHERE id = ?", (referrer_id,)) await db.commit() else: # Handle existing users who missed the reward user = dict(user_row) if not user.get('is_rewarded'): cursor = await db.execute("SELECT COUNT(*) FROM users") count = (await cursor.fetchone())[0] if count < 200: await db.execute( "UPDATE users SET points = points + 10000, is_rewarded = 1 WHERE id = ?", (user_id,) ) await db.commit() logger.info(f"Existing User {user_id} awarded 10,000 pts (Count: {count})") cursor = await db.execute("SELECT * FROM users WHERE id = ?", (user_id,)) user = await cursor.fetchone() return dict(user) @router.get("/status/{user_id}", response_model=UserStatus) async def get_user_status(user_id: str): async with db_manager.get_connection() as db: cursor = await db.execute("SELECT * FROM users WHERE id = ?", (user_id,)) user = await cursor.fetchone() if not user: return await init_user(user_id) return dict(user) @router.post("/watch") async def track_watch(user_id: str, minutes: int = 5): """Real system: 1 Hour = 120 Points (2 points per minute)""" points_per_min = 2 pts_to_add = minutes * points_per_min async with db_manager.get_connection() as db: await db.execute( "UPDATE users SET watch_time_total = watch_time_total + ?, points = points + ? WHERE id = ?", (minutes * 60, pts_to_add, user_id) ) # Performance Milestone: Every 2 real hours of watching award 200 extra bonus points cursor = await db.execute("SELECT watch_time_total, last_watch_reward_time, points FROM users WHERE id = ?", (user_id,)) user_at = await cursor.fetchone() milestone = 2 * 3600 # 2 hours if user_at["watch_time_total"] // milestone > user_at["last_watch_reward_time"] // milestone: await db.execute( "UPDATE users SET points = points + 200, last_watch_reward_time = ? WHERE id = ?", (user_at["watch_time_total"], user_id) ) await db.commit() return {"status": "success", "added": pts_to_add, "total_points": user_at["points"] + pts_to_add} @router.post("/share-click") async def register_share_click(referrer_id: str): """Award points for just clicks on shared links""" async with db_manager.get_connection() as db: await db.execute( "UPDATE users SET points = points + 5 WHERE id = ?", (referrer_id,) ) await db.commit() return {"status": "success"} @router.post("/redeem") async def redeem_reward(user_id: str, reward_type: str): """Redemption logic: Daily, Weekly, Monthly ad-free subscriptions""" costs = { "ad_free_1d": 700, # 1 Day "ad_free_1w": 3000, # 1 Week "ad_free_1m": 10000, # 1 Month "fan_badge": 100000 # Permanent Ultra Fan Badge } durations = { "ad_free_1d": 1 * 24 * 3600, "ad_free_1w": 7 * 24 * 3600, "ad_free_1m": 30 * 24 * 3600 } cost = costs.get(reward_type) if not cost: raise HTTPException(status_code=400, detail="Invalid reward type") async with db_manager.get_connection() as db: cursor = await db.execute("SELECT points, ad_free_until FROM users WHERE id = ?", (user_id,)) user = await cursor.fetchone() if not user or user["points"] < cost: raise HTTPException(status_code=403, detail="Insufficient points") if reward_type.startswith("ad_free"): now = int(time.time()) base_time = max(now, user["ad_free_until"] or 0) new_expiry = base_time + durations[reward_type] await db.execute( "UPDATE users SET points = points - ?, ad_free_until = ? WHERE id = ?", (cost, new_expiry, user_id) ) elif reward_type == "fan_badge": await db.execute( "UPDATE users SET points = points - ?, is_fan = 1 WHERE id = ?", (cost, user_id) ) await db.commit() return {"status": "success", "reward": reward_type, "new_points": user["points"] - cost} @router.post("/redeem-promo") async def redeem_promo(user_id: str, code: str): """Redeem a promo code for ad-free subscription""" code = code.strip() now = int(time.time()) # Special Hardcoded Codes if code == "movidovip1": # Lifetime ad-free lifetime_expiry = 2147483647 # Year 2038 (standard unix limit) async with db_manager.get_connection() as db: await db.execute("UPDATE users SET ad_free_until = ? WHERE id = ?", (lifetime_expiry, user_id)) await db.commit() return {"status": "success", "reward": "lifetime", "new_until": lifetime_expiry} if code == "vip-movido34": # 90 days, 300 uses limit async with db_manager.get_connection() as db: # Check if user already used this code cursor = await db.execute("SELECT * FROM user_promos WHERE user_id = ? AND code = ?", (user_id, code)) if await cursor.fetchone(): raise HTTPException(status_code=400, detail="لقد استخدمت هذا الكود بالفعل") # Check usage count cursor = await db.execute("SELECT COUNT(*) FROM user_promos WHERE code = ?", (code,)) usage_count = (await cursor.fetchone())[0] if usage_count >= 300: raise HTTPException(status_code=400, detail="عذراً، وصل هذا الكود للحد الأقصى من الاستخدام") # Award 90 days cursor = await db.execute("SELECT ad_free_until FROM users WHERE id = ?", (user_id,)) user = await cursor.fetchone() current_until = max(user["ad_free_until"] or 0, now) new_until = current_until + (90 * 24 * 3600) await db.execute("INSERT INTO user_promos (user_id, code) VALUES (?, ?)", (user_id, code)) await db.execute("UPDATE users SET ad_free_until = ? WHERE id = ?", (new_until, user_id)) await db.commit() return {"status": "success", "reward": "90_days", "new_until": new_until} # Standard DB codes (backup) async with db_manager.get_connection() as db: cursor = await db.execute("SELECT * FROM promo_codes WHERE code = ? AND is_active = 1", (code,)) promo = await cursor.fetchone() if not promo: raise HTTPException(status_code=404, detail="كود غير صالح") if promo["current_uses"] >= promo["max_uses"]: raise HTTPException(status_code=400, detail="انتهت صلاحية هذا الكود") # Check if user already used it cursor = await db.execute("SELECT * FROM user_promos WHERE user_id = ? AND code = ?", (user_id, code)) if await cursor.fetchone(): raise HTTPException(status_code=400, detail="لقد استخدمت هذا الكود بالفعل") duration = (promo["duration_days"] or 30) * 24 * 3600 cursor = await db.execute("SELECT ad_free_until FROM users WHERE id = ?", (user_id,)) user = await cursor.fetchone() current_until = max(user["ad_free_until"] or 0, now) new_until = current_until + duration await db.execute("INSERT INTO user_promos (user_id, code) VALUES (?, ?)", (user_id, code)) await db.execute("UPDATE promo_codes SET current_uses = current_uses + 1 WHERE code = ?", (code,)) await db.execute("UPDATE users SET ad_free_until = ? WHERE id = ?", (new_until, user_id)) await db.commit() return {"status": "success", "reward": promo["reward_type"], "new_until": new_until} @router.post("/history") async def save_history(user_id: str, content_id: str, content_type: str, episode_id: Optional[str] = None, progress: int = 0): async with db_manager.get_connection() as db: # Check if entry exists for this user/content combination to avoid duplicates or just log everything? # User request: "store the link I watched... and make seo" # We will insert a new record for every watch session to track frequency, # OR update the last seen time. Let's do INSERT for full history log. await db.execute( """INSERT INTO watch_history (user_id, content_id, content_type, episode_id, progress) VALUES (?, ?, ?, ?, ?)""", (user_id, content_id, content_type, episode_id, progress) ) await db.commit() return {"status": "saved"} @router.get("/history/{user_id}") async def get_history(user_id: str): async with db_manager.get_connection() as db: cursor = await db.execute( """SELECT h.*, s.title, s.poster FROM watch_history h LEFT JOIN series s ON h.content_id = s.id AND h.content_type = 'series' -- Join movies if we had a movies table populated similarly WHERE h.user_id = ? ORDER BY h.watched_at DESC LIMIT 50""", (user_id,) ) history = await cursor.fetchall() return [dict(row) for row in history]