from fastapi import FastAPI, APIRouter, HTTPException, Depends, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from starlette.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
import os
import logging
from pathlib import Path
from pydantic import BaseModel, Field, EmailStr
from typing import List, Optional
import uuid
from datetime import datetime, timedelta, timezone
from passlib.context import CryptContext
from jose import JWTError, jwt
from dotenv import load_dotenv
import hashlib

ROOT_DIR = Path(__file__).parent
load_dotenv(ROOT_DIR / '.env')

# MongoDB connection
mongo_url = os.environ['MONGO_URL']
client = AsyncIOMotorClient(mongo_url)
db = client[os.environ['DB_NAME']]

# Security
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
security = HTTPBearer()
SECRET_KEY = os.environ.get("SECRET_KEY", "your-secret-key-change-in-production")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# Create the main app
app = FastAPI(title="Trainings 24x7 - Cyber Crime Awareness Platform")

# Create a router with the /api prefix
api_router = APIRouter(prefix="/api")

# Auth Models
class UserRegister(BaseModel):
    username: str
    email: EmailStr
    password: str

class UserLogin(BaseModel):
    email: EmailStr
    password: str

class User(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    username: str
    email: EmailStr
    is_active: bool = True
    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    total_points: int = 0
    badges: List[str] = []
    games_completed: List[str] = []
    courses_enrolled: List[str] = []

class Token(BaseModel):
    access_token: str
    token_type: str
    user: dict

# Game Models
class GameScore(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    user_id: str
    game_name: str
    score: int
    time_taken: int  # in seconds
    completed: bool
    completed_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))

class Badge(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    name: str
    description: str
    icon: str
    requirements: dict

class Leaderboard(BaseModel):
    user_id: str
    username: str
    total_points: int
    games_completed: int
    badges_count: int

# Course Models
class Course(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    title: str
    description: str
    level: str  # foundation, warrior, ai_ml
    duration_weeks: int
    modules: List[dict]
    price: float = 0.0

class Enrollment(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    user_id: str
    course_id: str
    enrolled_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    progress: int = 0  # percentage

# Helper functions
def verify_password(plain_password, hashed_password):
    # Truncate password if too long for bcrypt
    if len(plain_password.encode('utf-8')) > 72:
        plain_password = plain_password[:72]
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    # Truncate password if too long for bcrypt
    if len(password.encode('utf-8')) > 72:
        password = password[:72]
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=[ALGORITHM])
        user_id: str = payload.get("sub")
        if user_id is None:
            raise credentials_exception
        user = await db.users.find_one({"id": user_id})
        if user is None:
            raise credentials_exception
        return User(**user)
    except JWTError:
        raise credentials_exception

# Auth Routes
@api_router.post("/auth/register", response_model=Token)
async def register_user(user_data: UserRegister):
    try:
        # Check if user exists
        existing_user = await db.users.find_one({"email": user_data.email})
        if existing_user:
            raise HTTPException(status_code=400, detail="Email already registered")
        
        # Validate password length
        if len(user_data.password) < 6:
            raise HTTPException(status_code=400, detail="Password must be at least 6 characters")
        
        # Create new user
        hashed_password = get_password_hash(user_data.password)
        new_user = User(
            username=user_data.username,
            email=user_data.email,
        )
        
        user_dict = new_user.dict()
        user_dict["hashed_password"] = hashed_password
        
        await db.users.insert_one(user_dict)
        
        # Create access token
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": new_user.id}, expires_delta=access_token_expires
        )
        
        return Token(
            access_token=access_token,
            token_type="bearer",
            user=new_user.dict()
        )
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"Registration error: {str(e)}")
        raise HTTPException(status_code=500, detail="Registration failed")

@api_router.post("/auth/login", response_model=Token)
async def login_user(user_data: UserLogin):
    try:
        user = await db.users.find_one({"email": user_data.email})
        if not user or not verify_password(user_data.password, user["hashed_password"]):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Incorrect email or password",
            )
        
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": user["id"]}, expires_delta=access_token_expires
        )
        
        user_obj = User(**user)
        return Token(
            access_token=access_token,
            token_type="bearer",
            user=user_obj.dict()
        )
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"Login error: {str(e)}")
        raise HTTPException(status_code=500, detail="Login failed")

@api_router.get("/auth/me", response_model=User)
async def get_user_profile(current_user: User = Depends(get_current_user)):
    return current_user

# Game Routes
@api_router.get("/games")
async def get_games():
    games = [
        {
            "id": "otp-fraud",
            "name": "OTP Fraud Awareness",
            "description": "Learn to identify and prevent OTP fraud attempts",
            "category": "fraud-prevention",
            "difficulty": "beginner",
            "estimated_time": 5,
            "thumbnail": "/api/placeholder/400/250"
        },
        {
            "id": "phishing-detection",
            "name": "Phishing Email Detective",
            "description": "Spot phishing emails before they fool you",
            "category": "email-security",
            "difficulty": "intermediate",
            "estimated_time": 8,
            "thumbnail": "/api/placeholder/400/250"
        },
        {
            "id": "social-media-doxing",
            "name": "Social Media Privacy Guard",
            "description": "Protect yourself from doxing and information gathering",
            "category": "privacy-protection",
            "difficulty": "advanced",
            "estimated_time": 10,
            "thumbnail": "/api/placeholder/400/250"
        }
    ]
    return games

@api_router.post("/games/score", response_model=GameScore)
async def submit_game_score(
    score_data: GameScore,
    current_user: User = Depends(get_current_user)
):
    score_data.user_id = current_user.id
    
    # Calculate points based on game performance
    points_earned = score_data.score * 10
    if score_data.completed:
        points_earned += 100  # Bonus for completion
    
    # Update user points and completed games
    await db.users.update_one(
        {"id": current_user.id},
        {
            "$inc": {"total_points": points_earned},
            "$addToSet": {"games_completed": score_data.game_name}
        }
    )
    
    # Save game score
    await db.game_scores.insert_one(score_data.dict())
    
    # Check for new badges
    await check_and_award_badges(current_user.id)
    
    return score_data

async def check_and_award_badges(user_id: str):
    user = await db.users.find_one({"id": user_id})
    if not user:
        return
    
    new_badges = []
    
    # First Game Badge
    if len(user.get("games_completed", [])) >= 1 and "first-game" not in user.get("badges", []):
        new_badges.append("first-game")
    
    # Game Master Badge
    if len(user.get("games_completed", [])) >= 3 and "game-master" not in user.get("badges", []):
        new_badges.append("game-master")
    
    # Points Milestone Badges
    if user.get("total_points", 0) >= 1000 and "points-1000" not in user.get("badges", []):
        new_badges.append("points-1000")
    
    if new_badges:
        await db.users.update_one(
            {"id": user_id},
            {"$addToSet": {"badges": {"$each": new_badges}}}
        )

@api_router.get("/leaderboard")
async def get_leaderboard(limit: int = 10):
    pipeline = [
        {
            "$project": {
                "username": 1,
                "total_points": 1,
                "games_completed": {"$size": {"$ifNull": ["$games_completed", []]}},
                "badges_count": {"$size": {"$ifNull": ["$badges", []]}}
            }
        },
        {"$sort": {"total_points": -1}},
        {"$limit": limit}
    ]
    
    leaderboard = await db.users.aggregate(pipeline).to_list(length=limit)
    return [
        {
            "user_id": user["_id"],
            "username": user["username"],
            "total_points": user["total_points"],
            "games_completed": user["games_completed"],
            "badges_count": user["badges_count"]
        }
        for user in leaderboard
    ]

@api_router.get("/badges")
async def get_available_badges():
    badges = [
        {
            "id": "first-game",
            "name": "First Steps",
            "description": "Complete your first cybercrime awareness game",
            "icon": "🎯",
            "category": "achievement"
        },
        {
            "id": "game-master",
            "name": "Game Master",
            "description": "Complete 3 different cybercrime awareness games",
            "icon": "🏆",
            "category": "achievement"
        },
        {
            "id": "points-1000",
            "name": "Point Collector",
            "description": "Earn 1000+ points",
            "icon": "💎",
            "category": "milestone"
        },
        {
            "id": "cyber-guardian",
            "name": "Cyber Guardian",
            "description": "Perfect score on all beginner games",
            "icon": "🛡️",
            "category": "mastery"
        }
    ]
    return badges

# Course Routes
@api_router.get("/courses")
async def get_courses():
    courses = [
        {
            "id": "foundation-cybersecurity",
            "title": "Foundation Level Cybersecurity",
            "description": "12-week comprehensive introduction to cybersecurity fundamentals",
            "level": "foundation",
            "duration_weeks": 12,
            "modules": [
                {"name": "Information Security Fundamentals", "duration": "4 hours"},
                {"name": "Network Security Basics", "duration": "4 hours"},
                {"name": "Threat Landscape Overview", "duration": "4 hours"},
                {"name": "Incident Response Basics", "duration": "4 hours"}
            ],
            "price": 199.00,
            "thumbnail": "/api/placeholder/300/200"
        },
        {
            "id": "warrior-cybersecurity",
            "title": "Warrior Level Cybersecurity",
            "description": "20-week advanced cybersecurity training with OSINT tools",
            "level": "warrior",
            "duration_weeks": 20,
            "modules": [
                {"name": "Advanced Threat Hunting", "duration": "8 hours"},
                {"name": "Digital Forensics", "duration": "8 hours"},
                {"name": "OSINT Tools Suite", "duration": "8 hours"},
                {"name": "Penetration Testing", "duration": "8 hours"}
            ],
            "price": 499.00,
            "thumbnail": "/api/placeholder/300/200"
        },
        {
            "id": "ai-ml-datascience",
            "title": "AI/ML & Data Science Track",
            "description": "16-week comprehensive AI and machine learning curriculum",
            "level": "ai_ml",
            "duration_weeks": 16,
            "modules": [
                {"name": "Python for Data Science", "duration": "4 hours"},
                {"name": "Statistics & Probability", "duration": "4 hours"},
                {"name": "Machine Learning Fundamentals", "duration": "4 hours"},
                {"name": "Deep Learning & Neural Networks", "duration": "4 hours"}
            ],
            "price": 399.00,
            "thumbnail": "/api/placeholder/300/200"
        }
    ]
    return courses

@api_router.post("/courses/{course_id}/enroll")
async def enroll_in_course(
    course_id: str,
    current_user: User = Depends(get_current_user)
):
    # Check if already enrolled
    existing_enrollment = await db.enrollments.find_one({
        "user_id": current_user.id,
        "course_id": course_id
    })
    
    if existing_enrollment:
        raise HTTPException(status_code=400, detail="Already enrolled in this course")
    
    enrollment = Enrollment(
        user_id=current_user.id,
        course_id=course_id
    )
    
    await db.enrollments.insert_one(enrollment.dict())
    
    # Update user's enrolled courses
    await db.users.update_one(
        {"id": current_user.id},
        {"$addToSet": {"courses_enrolled": course_id}}
    )
    
    return {"message": "Successfully enrolled in course", "enrollment": enrollment.dict()}

@api_router.get("/courses/my-enrollments")
async def get_my_enrollments(current_user: User = Depends(get_current_user)):
    enrollments = await db.enrollments.find({"user_id": current_user.id}).to_list(length=100)
    return enrollments

# Dashboard Routes
@api_router.get("/dashboard/stats")
async def get_dashboard_stats(current_user: User = Depends(get_current_user)):
    # Get user's game scores
    game_scores = await db.game_scores.find({"user_id": current_user.id}).to_list(length=100)
    
    # Calculate stats
    total_games = len(set([score["game_name"] for score in game_scores]))
    total_score = sum([score["score"] for score in game_scores])
    avg_score = total_score / len(game_scores) if game_scores else 0
    
    # Get recent activities
    recent_activities = await db.game_scores.find(
        {"user_id": current_user.id}
    ).sort("completed_at", -1).limit(5).to_list(length=5)
    
    return {
        "user": current_user.dict(),
        "stats": {
            "total_points": current_user.total_points,
            "games_completed": total_games,
            "badges_earned": len(current_user.badges),
            "courses_enrolled": len(current_user.courses_enrolled),
            "average_score": round(avg_score, 2)
        },
        "recent_activities": recent_activities
    }

# Health check
@api_router.get("/")
async def root():
    return {"message": "Trainings 24x7 - Cyber Crime Awareness Platform API", "status": "running"}

# Include the router in the main app
app.include_router(api_router)

app.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_origins=os.environ.get('CORS_ORIGINS', '*').split(','),
    allow_methods=["*"],
    allow_headers=["*"],
)

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

@app.on_event("shutdown")
async def shutdown_db_client():
    client.close()