from fastapi import FastAPI, APIRouter, HTTPException, Depends, Header
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from dotenv import load_dotenv
from starlette.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
from jose import jwt, JWTError
from passlib.context import CryptContext
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


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']]

# JWT Configuration
JWT_SECRET = os.getenv("JWT_SECRET", "your-secret-key-change-in-production")
JWT_ALGORITHM = "HS256"
JWT_EXPIRATION_HOURS = 720  # 30 days

# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
security = HTTPBearer()

# Create the main app without a prefix
app = FastAPI()

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


# Define Models
class StatusCheck(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    client_name: str
    timestamp: datetime = Field(default_factory=datetime.utcnow)

class StatusCheckCreate(BaseModel):
    client_name: str

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

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

class UserResponse(BaseModel):
    id: str
    username: str
    email: str
    plan_tier: str = "free"
    is_admin: bool = False

class TokenResponse(BaseModel):
    access_token: str
    token_type: str = "bearer"
    user: UserResponse

# Task Models
class TaskCreate(BaseModel):
    title: str
    priority: str = "normal"  # critical, high, normal, low
    life_area: Optional[str] = None
    due_date: Optional[str] = None  # YYYY-MM-DD
    due_time: Optional[str] = None  # HH:MM
    duration: Optional[int] = None  # minutes
    goal_id: Optional[str] = None
    notes: Optional[str] = None
    recurrence: Optional[str] = None  # daily, weekly, monthly
    status: str = "pending"  # pending, completed, deleted

class TaskUpdate(BaseModel):
    title: Optional[str] = None
    priority: Optional[str] = None
    life_area: Optional[str] = None
    due_date: Optional[str] = None
    due_time: Optional[str] = None
    duration: Optional[int] = None
    goal_id: Optional[str] = None
    notes: Optional[str] = None
    recurrence: Optional[str] = None
    status: Optional[str] = None

class TaskResponse(BaseModel):
    id: str
    user_id: str
    title: str
    priority: str
    life_area: Optional[str] = None
    due_date: Optional[str] = None
    due_time: Optional[str] = None
    duration: Optional[int] = None
    goal_id: Optional[str] = None
    notes: Optional[str] = None
    recurrence: Optional[str] = None
    status: str
    created_at: datetime
    updated_at: datetime
    completed_at: Optional[datetime] = None

# Auth Helper Functions
def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

def create_access_token(data: dict) -> str:
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(hours=JWT_EXPIRATION_HOURS)
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)

async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    try:
        token = credentials.credentials
        payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
        user_id = payload.get("sub")
        if user_id is None:
            raise HTTPException(status_code=401, detail="Invalid authentication credentials")
        
        user = await db.users.find_one({"id": user_id})
        if user is None:
            raise HTTPException(status_code=401, detail="User not found")
        
        return user
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid authentication credentials")

# Add your routes to the router instead of directly to app
@api_router.get("/")
async def root():
    return {"message": "Hello World"}

# Auth Endpoints
@api_router.post("/auth/register", response_model=TokenResponse)
async def register(user_data: UserRegister):
    # Check if user already exists
    existing_user = await db.users.find_one({"email": user_data.email})
    if existing_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    
    # Create new user
    user_id = str(uuid.uuid4())
    user = {
        "id": user_id,
        "username": user_data.username,
        "email": user_data.email,
        "password": hash_password(user_data.password),
        "plan_tier": "free",
        "is_admin": False,
        "created_at": datetime.utcnow()
    }
    
    await db.users.insert_one(user)
    
    # Create access token
    access_token = create_access_token({"sub": user_id})
    
    return TokenResponse(
        access_token=access_token,
        user=UserResponse(**{k: v for k, v in user.items() if k != 'password'})
    )

@api_router.post("/auth/login", response_model=TokenResponse)
async def login(user_data: UserLogin):
    # Find user by email
    user = await db.users.find_one({"email": user_data.email})
    if not user or not verify_password(user_data.password, user["password"]):
        raise HTTPException(status_code=401, detail="Invalid email or password")
    
    # Create access token
    access_token = create_access_token({"sub": user["id"]})
    
    return TokenResponse(
        access_token=access_token,
        user=UserResponse(**{k: v for k, v in user.items() if k != 'password'})
    )

@api_router.get("/auth/me", response_model=UserResponse)
async def get_me(current_user = Depends(get_current_user)):
    return UserResponse(**{k: v for k, v in current_user.items() if k != 'password'})

@api_router.post("/auth/reset-password")
async def reset_password(email: EmailStr):
    # In production, send email with reset link
    # For now, just return success
    return {"message": "Password reset link sent to email"}

# Dashboard Endpoint
@api_router.get("/dashboard")
async def get_dashboard(current_user = Depends(get_current_user)):
    user_id = current_user["id"]
    
    # Get tasks for today
    today = datetime.utcnow().date().isoformat()
    tasks = await db.tasks.find({
        "user_id": user_id, 
        "status": {"$ne": "deleted"},
        "$or": [
            {"due_date": today},
            {"due_date": {"$lt": today}, "status": "pending"}
        ]
    }).limit(5).to_list(5)
    
    # Get habits
    habits = await db.habits.find({"user_id": user_id}).limit(5).to_list(5)
    
    # Get goals
    goals_count = await db.goals.count_documents({"user_id": user_id, "status": "active"})
    
    # Count tasks
    tasks_today_count = await db.tasks.count_documents({
        "user_id": user_id,
        "status": "pending",
        "$or": [
            {"due_date": today},
            {"due_date": {"$lt": today}}
        ]
    })
    
    return {
        "tasks_today": tasks_today_count,
        "habits_done": 0,
        "habits_total": len(habits),
        "goals_active": goals_count,
        "critical_alerts": 0,
        "tasks": tasks,
        "habits": habits,
    }

# Task Endpoints
@api_router.get("/tasks")
async def get_tasks(
    current_user = Depends(get_current_user),
    filter: Optional[str] = "all",  # today, upcoming, all, area
    life_area: Optional[str] = None,
    sort_by: Optional[str] = "due_date",  # due_date, priority, life_area, created_at
    status: Optional[str] = "pending"  # pending, completed, all
):
    user_id = current_user["id"]
    query = {"user_id": user_id}
    
    # Filter by status
    if status == "all":
        query["status"] = {"$ne": "deleted"}
    else:
        query["status"] = status
    
    # Apply date filters
    today = datetime.utcnow().date().isoformat()
    if filter == "today":
        query["$or"] = [
            {"due_date": today},
            {"due_date": {"$lt": today}, "status": "pending"}
        ]
    elif filter == "upcoming":
        query["due_date"] = {"$gte": today}
    elif filter == "area" and life_area:
        query["life_area"] = life_area
    
    # Sort mapping
    sort_field = sort_by if sort_by in ["due_date", "priority", "life_area", "created_at"] else "due_date"
    
    tasks = await db.tasks.find(query).sort(sort_field, 1).to_list(1000)
    return tasks

@api_router.post("/tasks", response_model=TaskResponse)
async def create_task(task_data: TaskCreate, current_user = Depends(get_current_user)):
    user_id = current_user["id"]
    
    task = {
        "id": str(uuid.uuid4()),
        "user_id": user_id,
        **task_data.dict(),
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
        "completed_at": None
    }
    
    await db.tasks.insert_one(task)
    return TaskResponse(**task)

@api_router.get("/tasks/{task_id}", response_model=TaskResponse)
async def get_task(task_id: str, current_user = Depends(get_current_user)):
    user_id = current_user["id"]
    
    task = await db.tasks.find_one({"id": task_id, "user_id": user_id})
    if not task:
        raise HTTPException(status_code=404, detail="Task not found")
    
    return TaskResponse(**task)

@api_router.put("/tasks/{task_id}", response_model=TaskResponse)
async def update_task(
    task_id: str,
    task_data: TaskUpdate,
    current_user = Depends(get_current_user)
):
    user_id = current_user["id"]
    
    # Find existing task
    task = await db.tasks.find_one({"id": task_id, "user_id": user_id})
    if not task:
        raise HTTPException(status_code=404, detail="Task not found")
    
    # Update fields
    update_data = {k: v for k, v in task_data.dict().items() if v is not None}
    update_data["updated_at"] = datetime.utcnow()
    
    await db.tasks.update_one(
        {"id": task_id, "user_id": user_id},
        {"$set": update_data}
    )
    
    updated_task = await db.tasks.find_one({"id": task_id, "user_id": user_id})
    return TaskResponse(**updated_task)

@api_router.delete("/tasks/{task_id}")
async def delete_task(task_id: str, current_user = Depends(get_current_user)):
    user_id = current_user["id"]
    
    # Soft delete - mark as deleted
    result = await db.tasks.update_one(
        {"id": task_id, "user_id": user_id},
        {"$set": {"status": "deleted", "updated_at": datetime.utcnow()}}
    )
    
    if result.modified_count == 0:
        raise HTTPException(status_code=404, detail="Task not found")
    
    return {"message": "Task deleted successfully"}

@api_router.patch("/tasks/{task_id}/complete")
async def complete_task(task_id: str, current_user = Depends(get_current_user)):
    user_id = current_user["id"]
    
    result = await db.tasks.update_one(
        {"id": task_id, "user_id": user_id},
        {"$set": {
            "status": "completed",
            "completed_at": datetime.utcnow(),
            "updated_at": datetime.utcnow()
        }}
    )
    
    if result.modified_count == 0:
        raise HTTPException(status_code=404, detail="Task not found")
    
    return {"message": "Task completed successfully"}

@api_router.patch("/tasks/{task_id}/uncomplete")
async def uncomplete_task(task_id: str, current_user = Depends(get_current_user)):
    user_id = current_user["id"]
    
    result = await db.tasks.update_one(
        {"id": task_id, "user_id": user_id},
        {"$set": {
            "status": "pending",
            "completed_at": None,
            "updated_at": datetime.utcnow()
        }}
    )
    
    if result.modified_count == 0:
        raise HTTPException(status_code=404, detail="Task not found")
    
    return {"message": "Task marked as pending"}

@api_router.post("/status", response_model=StatusCheck)
async def create_status_check(input: StatusCheckCreate):
    status_dict = input.dict()
    status_obj = StatusCheck(**status_dict)
    _ = await db.status_checks.insert_one(status_obj.dict())
    return status_obj

@api_router.get("/status", response_model=List[StatusCheck])
async def get_status_checks():
    status_checks = await db.status_checks.find().to_list(1000)
    return [StatusCheck(**status_check) for status_check in status_checks]

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

app.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_origins=["*"],
    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()
