import random
from typing import List, Optional
from datetime import timedelta

from fastapi import FastAPI, HTTPException, Query, Depends, Response, Request
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.sessions import SessionMiddleware
from pydantic import BaseModel, EmailStr, field_validator
import os

import uvicorn

from db_utils import (
    list_recipes_db,
    create_recipe_db,
    get_recipes_by_filters_db,
    update_recipe_db,
    delete_recipe_db,
    get_conn,
)

from auth_utils import (
    hash_password,
    verify_password,
    create_access_token,
    get_current_user,
    ACCESS_TOKEN_EXPIRE_MINUTES,
)

from user_db_utils import (
    create_user,
    get_user_by_username,
    get_user_by_email,
    update_user_auth_provider,
)

from grocery_db_utils import (
    create_grocery_list,
    get_user_grocery_lists,
    get_grocery_list_by_id,
    update_grocery_list,
    delete_grocery_list,
    share_grocery_list,
    unshare_grocery_list,
    get_grocery_list_shares,
    search_users,
    toggle_grocery_list_pin,
    update_share_permission,
)

from notification_db_utils import (
    create_notification,
    get_user_notifications,
    mark_notification_as_read,
    mark_all_notifications_as_read,
    delete_notification,
)

from backup_restore_api import (
    perform_backup,
    perform_restore,
    list_r2_backups,
    get_environment,
    get_r2_bucket,
)

from backup_scheduler import start_backup_scheduler, stop_backup_scheduler

from email_utils import (
    generate_verification_code,
    send_verification_email,
    store_verification_code,
    verify_code,
)

from oauth_utils import oauth


class RecipeBase(BaseModel):
    name: str
    meal_type: str          # breakfast / lunch / dinner / snack
    time_minutes: int
    made_by: Optional[str] = None  # Person who created this recipe version
    tags: List[str] = []
    ingredients: List[str] = []
    steps: List[str] = []
    image: Optional[str] = None  # Base64-encoded image or image URL


class RecipeCreate(RecipeBase):
    pass


class Recipe(RecipeBase):
    id: int
    user_id: Optional[int] = None  # Recipe owner ID
    owner_display_name: Optional[str] = None  # Owner's display name for filtering

    
class RecipeUpdate(RecipeBase):
    pass


# User models
class UserRegister(BaseModel):
    username: str
    email: EmailStr
    password: str
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    display_name: str
    
    @field_validator('username')
    @classmethod
    def username_must_be_english(cls, v: str) -> str:
        if not v.isascii():
            raise ValueError('שם משתמש חייב להיות באנגלית בלבד')
        if not all(c.isalnum() or c in '_-' for c in v):
            raise ValueError('שם משתמש יכול להכיל רק אותיות, מספרים, _ ו-')
        return v


class UserLogin(BaseModel):
    username: str
    password: str


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


class UserResponse(BaseModel):
    id: int
    username: str
    email: str
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    display_name: str
    is_admin: bool = False
    auth_provider: str = "local"


class RequestPasswordChangeCode(BaseModel):
    pass  # No fields needed, uses current user from token


class ChangePasswordRequest(BaseModel):
    verification_code: str
    current_password: str
    new_password: str


class ForgotPasswordRequest(BaseModel):
    email: EmailStr


class ResetPasswordRequest(BaseModel):
    token: str
    new_password: str


# Grocery List models
class GroceryListCreate(BaseModel):
    name: str
    items: List[str] = []


class GroceryListUpdate(BaseModel):
    name: Optional[str] = None
    items: Optional[List[str]] = None


class GroceryList(BaseModel):
    id: int
    name: str
    items: List[str]
    owner_id: int
    is_pinned: bool = False
    owner_display_name: Optional[str] = None
    can_edit: bool = False
    is_owner: bool = False
    created_at: str
    updated_at: str


class ShareGroceryList(BaseModel):
    user_identifier: str  # Can be username or display_name
    can_edit: bool = False


class UpdateSharePermission(BaseModel):
    can_edit: bool


class GroceryListShare(BaseModel):
    id: int
    list_id: int
    shared_with_user_id: int
    username: str
    display_name: str
    email: str
    can_edit: bool
    shared_at: str


class UserSearch(BaseModel):
    id: int
    username: str
    display_name: str
    email: str


class Notification(BaseModel):
    id: int
    user_id: int
    type: str
    message: str
    related_id: Optional[int] = None
    is_read: bool
    created_at: str


app = FastAPI(
    title="Random Recipes API",
    description="API פשוט לבחירת מתכונים לצהריים / ערב / כל ארוחה 😋",
)

# Add session middleware for OAuth (must be before other middleware)
app.add_middleware(
    SessionMiddleware,
    secret_key=os.getenv("SECRET_KEY", "your-secret-key-change-in-production-min-32-chars"),
    max_age=7200,  # 2 hours for OAuth flow
    same_site="lax",  # Use "lax" for localhost development
    https_only=False,  # Set to True in production with HTTPS
)

# Allow CORS from frontend domains
allowed_origins = [
    "http://localhost:5173",
    "http://localhost:5174",
    "http://localhost:3000",
    "https://my-recipes.dvirlabs.com",
    "http://my-recipes.dvirlabs.com",
    "http://my-recipes.aws-dvirlabs.com",
    "https://my-recipes.aws-dvirlabs.com"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=allowed_origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ===== Startup Event: Start Backup Scheduler =====
@app.on_event("startup")
async def startup_event():
    """Start the automatic backup scheduler when the app starts"""
    start_backup_scheduler()


@app.on_event("shutdown")
async def shutdown_event():
    """Stop the backup scheduler when the app shuts down"""
    stop_backup_scheduler()


@app.get("/recipes", response_model=List[Recipe])
def list_recipes():
    rows = list_recipes_db()
    recipes = [
        Recipe(
            id=r["id"],
            name=r["name"],
            meal_type=r["meal_type"],
            time_minutes=r["time_minutes"],
            made_by=r.get("made_by"),
            tags=r["tags"] or [],
            ingredients=r["ingredients"] or [],
            steps=r["steps"] or [],
            image=r.get("image"),
            user_id=r.get("user_id"),
            owner_display_name=r.get("owner_display_name"),
        )
        for r in rows
    ]
    return recipes


@app.post("/recipes", response_model=Recipe, status_code=201)
def create_recipe(recipe_in: RecipeCreate, current_user: dict = Depends(get_current_user)):
    data = recipe_in.dict()
    data["meal_type"] = data["meal_type"].lower()
    data["user_id"] = current_user["user_id"]

    row = create_recipe_db(data)

    return Recipe(
        id=row["id"],
        name=row["name"],
        meal_type=row["meal_type"],
        time_minutes=row["time_minutes"],
        made_by=row.get("made_by"),
        tags=row["tags"] or [],
        ingredients=row["ingredients"] or [],
        steps=row["steps"] or [],
        image=row.get("image"),
        user_id=row.get("user_id"),
        owner_display_name=current_user.get("display_name"),
    )

@app.put("/recipes/{recipe_id}", response_model=Recipe)
def update_recipe(recipe_id: int, recipe_in: RecipeUpdate, current_user: dict = Depends(get_current_user)):
    # Check ownership BEFORE updating (admins can edit any recipe)
    conn = get_conn()
    try:
        with conn.cursor() as cur:
            cur.execute("SELECT user_id FROM recipes WHERE id = %s", (recipe_id,))
            recipe = cur.fetchone()
            if not recipe:
                raise HTTPException(status_code=404, detail="המתכון לא נמצא")
            # Allow if user is owner OR admin
            if recipe["user_id"] != current_user["user_id"] and not current_user.get("is_admin", False):
                raise HTTPException(status_code=403, detail="אין לך הרשאה לערוך מתכון זה")
    finally:
        conn.close()
    
    data = recipe_in.dict()
    data["meal_type"] = data["meal_type"].lower()

    row = update_recipe_db(recipe_id, data)
    if not row:
        raise HTTPException(status_code=404, detail="המתכון לא נמצא")

    return Recipe(
        id=row["id"],
        name=row["name"],
        meal_type=row["meal_type"],
        time_minutes=row["time_minutes"],
        made_by=row.get("made_by"),
        tags=row["tags"] or [],
        ingredients=row["ingredients"] or [],
        steps=row["steps"] or [],
        image=row.get("image"),
        user_id=row.get("user_id"),
        owner_display_name=current_user.get("display_name"),
    )


@app.delete("/recipes/{recipe_id}", status_code=204)
def delete_recipe(recipe_id: int, current_user: dict = Depends(get_current_user)):
    # Get recipe first to check ownership (admins can delete any recipe)
    conn = get_conn()
    try:
        with conn.cursor() as cur:
            cur.execute("SELECT user_id FROM recipes WHERE id = %s", (recipe_id,))
            recipe = cur.fetchone()
            if not recipe:
                raise HTTPException(status_code=404, detail="המתכון לא נמצא")
            # Allow if user is owner OR admin
            if recipe["user_id"] != current_user["user_id"] and not current_user.get("is_admin", False):
                raise HTTPException(status_code=403, detail="אין לך הרשאה למחוק מתכון זה")
    finally:
        conn.close()
    
    deleted = delete_recipe_db(recipe_id)
    if not deleted:
        raise HTTPException(status_code=404, detail="המתכון לא נמצא")
    return


@app.get("/recipes/random", response_model=Recipe)
def random_recipe(
    meal_type: Optional[str] = None,
    max_time: Optional[int] = None,
    ingredients: Optional[List[str]] = Query(
        None,
        description="רשימת מרכיבים (ingredients=ביצה&ingredients=עגבניה...)",
    ),
):
    rows = get_recipes_by_filters_db(meal_type, max_time)

    recipes = [
        Recipe(
            id=r["id"],
            name=r["name"],
            meal_type=r["meal_type"],
            time_minutes=r["time_minutes"],
            made_by=r.get("made_by"),
            tags=r["tags"] or [],
            ingredients=r["ingredients"] or [],
            steps=r["steps"] or [],
            image=r.get("image"),
            user_id=r.get("user_id"),
            owner_display_name=r.get("owner_display_name"),
        )
        for r in rows
    ]

    # סינון לפי מרכיבים באפליקציה
    if ingredients:
        desired = {i.strip().lower() for i in ingredients if i.strip()}
        if desired:
            recipes = [
                r
                for r in recipes
                if desired.issubset({ing.lower() for ing in r.ingredients})
            ]

    if not recipes:
        raise HTTPException(
            status_code=404,
            detail="לא נמצאו מתכונים מתאימים לפילטרים שבחרת",
        )

    return random.choice(recipes)


# Authentication endpoints
@app.post("/auth/register", response_model=UserResponse, status_code=201)
def register(user: UserRegister):
    """Register a new user"""
    print(f"[REGISTER] Starting registration for username: {user.username}")
    
    # Check if username already exists
    print(f"[REGISTER] Checking if username exists...")
    existing_user = get_user_by_username(user.username)
    if existing_user:
        print(f"[REGISTER] Username already exists")
        raise HTTPException(
            status_code=400,
            detail="שם המשתמש כבר קיים במערכת"
        )
    
    # Check if email already exists
    print(f"[REGISTER] Checking if email exists...")
    existing_email = get_user_by_email(user.email)
    if existing_email:
        print(f"[REGISTER] Email already exists")
        raise HTTPException(
            status_code=400,
            detail="האימייל כבר רשום במערכת"
        )
    
    # Check if display_name already exists
    print(f"[REGISTER] Checking if display_name exists...")
    from user_db_utils import get_user_by_display_name
    existing_display_name = get_user_by_display_name(user.display_name)
    if existing_display_name:
        print(f"[REGISTER] Display name already exists")
        raise HTTPException(
            status_code=400,
            detail="שם התצוגה כבר קיים במערכת"
        )
    
    # Hash password and create user
    print(f"[REGISTER] Hashing password...")
    password_hash = hash_password(user.password)
    print(f"[REGISTER] Creating user in database...")
    new_user = create_user(
        user.username, 
        user.email, 
        password_hash,
        user.first_name,
        user.last_name,
        user.display_name
    )
    print(f"[REGISTER] User created successfully: {new_user['id']}")
    
    return UserResponse(
        id=new_user["id"],
        username=new_user["username"],
        email=new_user["email"],
        first_name=new_user.get("first_name"),
        last_name=new_user.get("last_name"),
        display_name=new_user["display_name"],
        is_admin=new_user.get("is_admin", False),
        auth_provider=new_user.get("auth_provider", "local")
    )


@app.post("/auth/login", response_model=Token)
def login(user: UserLogin):
    """Login user and return JWT token"""
    # Get user from database
    db_user = get_user_by_username(user.username)
    if not db_user:
        raise HTTPException(
            status_code=401,
            detail="שם משתמש או סיסמה שגויים"
        )
    
    # Verify password
    if not verify_password(user.password, db_user["password_hash"]):
        raise HTTPException(
            status_code=401,
            detail="שם משתמש או סיסמה שגויים"
        )
    
    # Create access token
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": str(db_user["id"]), "username": db_user["username"]},
        expires_delta=access_token_expires
    )
    
    return Token(access_token=access_token, token_type="bearer")


@app.get("/auth/me", response_model=UserResponse)
def get_me(current_user: dict = Depends(get_current_user)):
    """Get current logged-in user info"""
    from user_db_utils import get_user_by_id
    user = get_user_by_id(current_user["user_id"])
    if not user:
        raise HTTPException(status_code=404, detail="משתמש לא נמצא")
    
    return UserResponse(
        id=user["id"],
        username=user["username"],
        email=user["email"],
        first_name=user.get("first_name"),
        last_name=user.get("last_name"),
        display_name=user["display_name"],
        is_admin=user.get("is_admin", False),
        auth_provider=user.get("auth_provider", "local")
    )


@app.post("/auth/request-password-change-code")
async def request_password_change_code(
    current_user: dict = Depends(get_current_user)
):
    """Send verification code to user's email for password change"""
    from user_db_utils import get_user_by_id
    
    # Get user from database
    user = get_user_by_id(current_user["user_id"])
    if not user:
        raise HTTPException(status_code=404, detail="משתמש לא נמצא")
    
    # Generate verification code
    code = generate_verification_code()
    
    # Store code
    store_verification_code(current_user["user_id"], code)
    
    # Send email
    try:
        await send_verification_email(user["email"], code, "password_change")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"שגיאה בשליחת אימייל: {str(e)}")
    
    return {"message": "קוד אימות נשלח לכתובת המייל שלך"}


@app.post("/auth/change-password")
def change_password(
    request: ChangePasswordRequest,
    current_user: dict = Depends(get_current_user)
):
    """Change user password after verifying code and current password"""
    from user_db_utils import get_user_by_id
    
    # Get user from database
    user = get_user_by_id(current_user["user_id"])
    if not user:
        raise HTTPException(status_code=404, detail="משתמש לא נמצא")
    
    # Verify code
    if not verify_code(current_user["user_id"], request.verification_code):
        raise HTTPException(status_code=401, detail="קוד אימות שגוי או פג תוקף")
    
    # Verify current password
    if not verify_password(request.current_password, user["password_hash"]):
        raise HTTPException(status_code=401, detail="סיסמה נוכחית שגויה")
    
    # Hash new password
    new_password_hash = hash_password(request.new_password)
    
    # Update password in database
    conn = get_conn()
    cur = conn.cursor()
    try:
        cur.execute(
            "UPDATE users SET password_hash = %s WHERE id = %s",
            (new_password_hash, current_user["user_id"])
        )
        conn.commit()
    except Exception as e:
        conn.rollback()
        raise HTTPException(status_code=500, detail=f"שגיאה בעדכון סיסמה: {str(e)}")
    finally:
        cur.close()
        conn.close()
    
    return {"message": "הסיסמה עודכנה בהצלחה"}


# ============= Google OAuth Endpoints =============

@app.get("/auth/google/login")
async def google_login(request: Request):
    """Redirect to Google OAuth login"""
    redirect_uri = os.getenv("GOOGLE_REDIRECT_URI", "http://localhost:8000/auth/google/callback")
    return await oauth.google.authorize_redirect(request, redirect_uri)


@app.get("/auth/google/callback")
async def google_callback(request: Request):
    """Handle Google OAuth callback"""
    try:
        # Get token from Google
        token = await oauth.google.authorize_access_token(request)
        
        # Get user info from Google
        user_info = token.get('userinfo')
        if not user_info:
            raise HTTPException(status_code=400, detail="Failed to get user info from Google")
        
        email = user_info.get('email')
        google_id = user_info.get('sub')
        name = user_info.get('name', '')
        given_name = user_info.get('given_name', '')
        family_name = user_info.get('family_name', '')
        
        if not email:
            raise HTTPException(status_code=400, detail="Email not provided by Google")
        
        # Check if user exists
        existing_user = get_user_by_email(email)
        
        if existing_user:
            # User exists, log them in and update auth_provider
            user_id = existing_user["id"]
            username = existing_user["username"]
            
            # Update auth_provider if it's different
            if existing_user.get("auth_provider") != "google":
                update_user_auth_provider(user_id, "google")
        else:
            # Create new user
            # Generate username from email or name
            username = email.split('@')[0]
            
            # Check if username exists, add number if needed
            base_username = username
            counter = 1
            while get_user_by_username(username):
                username = f"{base_username}{counter}"
                counter += 1
            
            # Create user with random password (they'll use Google login)
            import secrets
            random_password = secrets.token_urlsafe(32)
            password_hash = hash_password(random_password)
            
            new_user = create_user(
                username=username,
                email=email,
                password_hash=password_hash,
                first_name=given_name if given_name else None,
                last_name=family_name if family_name else None,
                display_name=name if name else username,
                is_admin=False,
                auth_provider="google"
            )
            user_id = new_user["id"]
        
        # Create JWT token
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": str(user_id), "username": username},
            expires_delta=access_token_expires
        )
        
        # Redirect to frontend with token
        frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5174")
        return Response(
            status_code=302,
            headers={"Location": f"{frontend_url}?token={access_token}"}
        )
        
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Google authentication failed: {str(e)}")


# ============= Microsoft Entra ID (Azure AD) OAuth Endpoints =============

@app.get("/auth/azure/login")
async def azure_login(request: Request):
    """Redirect to Microsoft Entra ID OAuth login"""
    redirect_uri = os.getenv("AZURE_REDIRECT_URI", "http://localhost:8000/auth/azure/callback")
    return await oauth.azure.authorize_redirect(request, redirect_uri)


@app.get("/auth/azure/callback")
async def azure_callback(request: Request):
    """Handle Microsoft Entra ID OAuth callback"""
    try:
        # Get token from Azure
        token = await oauth.azure.authorize_access_token(request)
        
        # Get user info from Azure
        user_info = token.get('userinfo')
        if not user_info:
            raise HTTPException(status_code=400, detail="Failed to get user info from Microsoft")
        
        email = user_info.get('email') or user_info.get('preferred_username')
        azure_id = user_info.get('oid') or user_info.get('sub')
        name = user_info.get('name', '')
        given_name = user_info.get('given_name', '')
        family_name = user_info.get('family_name', '')
        
        if not email:
            raise HTTPException(status_code=400, detail="Email not provided by Microsoft")
        
        # Check if user exists
        existing_user = get_user_by_email(email)
        
        if existing_user:
            # User exists, log them in and update auth_provider
            user_id = existing_user["id"]
            username = existing_user["username"]
            
            # Update auth_provider if it's different
            if existing_user.get("auth_provider") != "azure":
                update_user_auth_provider(user_id, "azure")
        else:
            # Create new user
            # Generate username from email or name
            username = email.split('@')[0]
            
            # Check if username exists, add number if needed
            base_username = username
            counter = 1
            while get_user_by_username(username):
                username = f"{base_username}{counter}"
                counter += 1
            
            # Create user with random password (they'll use Azure login)
            import secrets
            random_password = secrets.token_urlsafe(32)
            password_hash = hash_password(random_password)
            
            new_user = create_user(
                username=username,
                email=email,
                password_hash=password_hash,
                first_name=given_name if given_name else None,
                last_name=family_name if family_name else None,
                display_name=name if name else username,
                is_admin=False,
                auth_provider="azure"
            )
            user_id = new_user["id"]
        
        # Create JWT token
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": str(user_id), "username": username},
            expires_delta=access_token_expires
        )
        
        # Redirect to frontend with token
        frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5174")
        return Response(
            status_code=302,
            headers={"Location": f"{frontend_url}?token={access_token}"}
        )
        
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Microsoft authentication failed: {str(e)}")


# ============= Password Reset Endpoints =============

@app.post("/forgot-password")
async def forgot_password(request: ForgotPasswordRequest):
    """Request password reset - send email with reset link"""
    try:
        # Check if user exists and is a local user
        user = get_user_by_email(request.email)
        
        if not user:
            # Don't reveal if user exists or not
            return {"message": "אם המייל קיים במערכת, נשלח אליך קישור לאיפוס סיסמה"}
        
        # Only allow password reset for local users
        if user.get("auth_provider") != "local":
            return {"message": "משתמשים שנרשמו דרך Google או Microsoft לא יכולים לאפס סיסמה"}
        
        # Generate reset token
        import secrets
        reset_token = secrets.token_urlsafe(32)
        
        # Store token
        from email_utils import store_password_reset_token, send_password_reset_email
        store_password_reset_token(request.email, reset_token)
        
        # Send email
        frontend_url = os.getenv("FRONTEND_URL", "http://localhost:5174")
        await send_password_reset_email(request.email, reset_token, frontend_url)
        
        return {"message": "אם המייל קיים במערכת, נשלח אליך קישור לאיפוס סיסמה"}
        
    except Exception as e:
        # Don't reveal errors to user
        print(f"Password reset error: {str(e)}")
        return {"message": "אם המייל קיים במערכת, נשלח אליך קישור לאיפוס סיסמה"}


@app.post("/reset-password")
async def reset_password(request: ResetPasswordRequest):
    """Reset password using token from email"""
    try:
        from email_utils import verify_reset_token, consume_reset_token
        
        # Verify token
        email = verify_reset_token(request.token)
        if not email:
            raise HTTPException(status_code=400, detail="הקישור לאיפוס סיסמה אינו תקף או שפג תוקפו")
        
        # Get user
        user = get_user_by_email(email)
        if not user:
            raise HTTPException(status_code=400, detail="משתמש לא נמצא")
        
        # Only allow password reset for local users
        if user.get("auth_provider") != "local":
            raise HTTPException(status_code=400, detail="משתמשים שנרשמו דרך Google או Microsoft לא יכולים לאפס סיסמה")
        
        # Update password
        password_hash = hash_password(request.new_password)
        conn = get_conn()
        cur = conn.cursor()
        cur.execute(
            "UPDATE users SET password_hash = %s WHERE email = %s",
            (password_hash, email)
        )
        conn.commit()
        cur.close()
        conn.close()
        
        # Consume token so it can't be reused
        consume_reset_token(request.token)
        
        return {"message": "הסיסמה עודכנה בהצלחה"}
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"שגיאה באיפוס סיסמה: {str(e)}")


# ============= Grocery Lists Endpoints =============

@app.get("/grocery-lists", response_model=List[GroceryList])
def list_grocery_lists(current_user: dict = Depends(get_current_user)):
    """Get all grocery lists owned by or shared with the current user"""
    lists = get_user_grocery_lists(current_user["user_id"])
    # Convert datetime objects to strings
    for lst in lists:
        lst["created_at"] = str(lst["created_at"])
        lst["updated_at"] = str(lst["updated_at"])
    return lists


@app.post("/grocery-lists", response_model=GroceryList, status_code=201)
def create_new_grocery_list(
    grocery_list: GroceryListCreate,
    current_user: dict = Depends(get_current_user)
):
    """Create a new grocery list"""
    new_list = create_grocery_list(
        owner_id=current_user["user_id"],
        name=grocery_list.name,
        items=grocery_list.items
    )
    new_list["owner_display_name"] = current_user.get("display_name")
    new_list["can_edit"] = True
    new_list["is_owner"] = True
    new_list["created_at"] = str(new_list["created_at"])
    new_list["updated_at"] = str(new_list["updated_at"])
    return new_list


@app.get("/grocery-lists/{list_id}", response_model=GroceryList)
def get_grocery_list(list_id: int, current_user: dict = Depends(get_current_user)):
    """Get a specific grocery list"""
    grocery_list = get_grocery_list_by_id(list_id, current_user["user_id"])
    if not grocery_list:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה או שאין לך גישה אליها")
    
    grocery_list["created_at"] = str(grocery_list["created_at"])
    grocery_list["updated_at"] = str(grocery_list["updated_at"])
    return grocery_list


@app.put("/grocery-lists/{list_id}", response_model=GroceryList)
def update_grocery_list_endpoint(
    list_id: int,
    grocery_list_update: GroceryListUpdate,
    current_user: dict = Depends(get_current_user)
):
    """Update a grocery list"""
    # Check if user has access
    existing = get_grocery_list_by_id(list_id, current_user["user_id"])
    if not existing:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    
    # Check if user has edit permission
    if not existing["can_edit"]:
        raise HTTPException(status_code=403, detail="אין לך הרשאה לערוך רשימת קניות זו")
    
    updated = update_grocery_list(
        list_id=list_id,
        name=grocery_list_update.name,
        items=grocery_list_update.items
    )
    
    if not updated:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    
    # Get full details with permissions
    result = get_grocery_list_by_id(list_id, current_user["user_id"])
    result["created_at"] = str(result["created_at"])
    result["updated_at"] = str(result["updated_at"])
    return result


@app.delete("/grocery-lists/{list_id}", status_code=204)
def delete_grocery_list_endpoint(list_id: int, current_user: dict = Depends(get_current_user)):
    """Delete a grocery list (owner only)"""
    # Check if user is owner
    existing = get_grocery_list_by_id(list_id, current_user["user_id"])
    if not existing:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    
    if not existing["is_owner"]:
        raise HTTPException(status_code=403, detail="רק הבעלים יכול למחוק רשימת קניות")
    
    deleted = delete_grocery_list(list_id)
    if not deleted:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    return


@app.patch("/grocery-lists/{list_id}/pin", response_model=GroceryList)
def toggle_pin_grocery_list_endpoint(list_id: int, current_user: dict = Depends(get_current_user)):
    """Toggle pin status for a grocery list (owner only)"""
    updated = toggle_grocery_list_pin(list_id, current_user["user_id"])
    if not updated:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה או שאין לך הרשאה")
    
    # Get full details with permissions
    result = get_grocery_list_by_id(list_id, current_user["user_id"])
    result["created_at"] = str(result["created_at"])
    result["updated_at"] = str(result["updated_at"])
    return result


@app.post("/grocery-lists/{list_id}/share", response_model=GroceryListShare)
def share_grocery_list_endpoint(
    list_id: int,
    share_data: ShareGroceryList,
    current_user: dict = Depends(get_current_user)
):
    """Share a grocery list with another user"""
    # Check if user is owner
    existing = get_grocery_list_by_id(list_id, current_user["user_id"])
    if not existing:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    
    if not existing["is_owner"]:
        raise HTTPException(status_code=403, detail="רק הבעלים יכול לשתף רשימת קניות")
    
    # Find user by username or display_name
    target_user = get_user_by_username(share_data.user_identifier)
    if not target_user:
        from user_db_utils import get_user_by_display_name
        target_user = get_user_by_display_name(share_data.user_identifier)
    
    if not target_user:
        raise HTTPException(status_code=404, detail="משתמש לא נמצא")
    
    # Don't allow sharing with yourself
    if target_user["id"] == current_user["user_id"]:
        raise HTTPException(status_code=400, detail="לא ניתן לשתף רשימה עם עצמך")
    
    # Share the list
    share = share_grocery_list(list_id, target_user["id"], share_data.can_edit)
    
    # Create notification for the user who received the share
    notification_message = f"רשימת קניות '{existing['name']}' שותפה איתך על ידי {current_user['display_name']}"
    create_notification(
        user_id=target_user["id"],
        type="grocery_share",
        message=notification_message,
        related_id=list_id
    )
    
    # Return with user details
    return GroceryListShare(
        id=share["id"],
        list_id=share["list_id"],
        shared_with_user_id=share["shared_with_user_id"],
        username=target_user["username"],
        display_name=target_user["display_name"],
        email=target_user["email"],
        can_edit=share["can_edit"],
        shared_at=str(share["shared_at"])
    )


@app.get("/grocery-lists/{list_id}/shares", response_model=List[GroceryListShare])
def get_grocery_list_shares_endpoint(
    list_id: int,
    current_user: dict = Depends(get_current_user)
):
    """Get all users a grocery list is shared with"""
    # Check if user is owner
    existing = get_grocery_list_by_id(list_id, current_user["user_id"])
    if not existing:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    
    if not existing["is_owner"]:
        raise HTTPException(status_code=403, detail="רק הבעלים יכול לראות את רשימת השיתופים")
    
    shares = get_grocery_list_shares(list_id)
    return [
        GroceryListShare(
            id=share["id"],
            list_id=share["list_id"],
            shared_with_user_id=share["shared_with_user_id"],
            username=share["username"],
            display_name=share["display_name"],
            email=share["email"],
            can_edit=share["can_edit"],
            shared_at=str(share["shared_at"])
        )
        for share in shares
    ]


@app.delete("/grocery-lists/{list_id}/shares/{user_id}", status_code=204)
def unshare_grocery_list_endpoint(
    list_id: int,
    user_id: int,
    current_user: dict = Depends(get_current_user)
):
    """Remove sharing access for a user"""
    # Check if user is owner
    existing = get_grocery_list_by_id(list_id, current_user["user_id"])
    if not existing:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    
    if not existing["is_owner"]:
        raise HTTPException(status_code=403, detail="רק הבעלים יכול להסיר שיתופים")
    
    deleted = unshare_grocery_list(list_id, user_id)
    if not deleted:
        raise HTTPException(status_code=404, detail="שיתוף לא נמצא")
    return


@app.patch("/grocery-lists/{list_id}/shares/{user_id}", response_model=GroceryListShare)
def update_share_permission_endpoint(
    list_id: int,
    user_id: int,
    share_data: UpdateSharePermission,
    current_user: dict = Depends(get_current_user)
):
    """Update share permissions for a user"""
    # Check if current user is the owner
    existing = get_grocery_list_by_id(list_id, current_user["user_id"])
    if not existing:
        raise HTTPException(status_code=404, detail="רשימת קניות לא נמצאה")
    
    if not existing["is_owner"]:
        raise HTTPException(status_code=403, detail="רק הבעלים יכול לשנות הרשאות")
    
    updated_share = update_share_permission(list_id, user_id, share_data.can_edit)
    if not updated_share:
        raise HTTPException(status_code=404, detail="שיתוף לא נמצא")
    
    # Get user details for response
    shares = get_grocery_list_shares(list_id)
    share_with_details = next((s for s in shares if s["shared_with_user_id"] == user_id), None)
    
    if not share_with_details:
        raise HTTPException(status_code=404, detail="שיתוף לא נמצא")
    
    return GroceryListShare(
        id=share_with_details["id"],
        list_id=list_id,
        shared_with_user_id=user_id,
        username=share_with_details["username"],
        display_name=share_with_details["display_name"],
        email=share_with_details["email"],
        can_edit=share_data.can_edit,
        shared_at=share_with_details["shared_at"].isoformat() if hasattr(share_with_details["shared_at"], 'isoformat') else str(share_with_details["shared_at"])
    )


@app.get("/users/search", response_model=List[UserSearch])
def search_users_endpoint(
    q: str = Query(..., min_length=1, description="Search query for username or display name"),
    current_user: dict = Depends(get_current_user)
):
    """Search users by username or display name for autocomplete"""
    users = search_users(q)
    return [
        UserSearch(
            id=user["id"],
            username=user["username"],
            display_name=user["display_name"],
            email=user["email"]
        )
        for user in users
    ]


# ===========================
# Notification Endpoints
# ===========================

@app.get("/notifications", response_model=List[Notification])
def get_notifications_endpoint(
    unread_only: bool = Query(False, description="Get only unread notifications"),
    current_user: dict = Depends(get_current_user)
):
    """Get all notifications for the current user"""
    notifications = get_user_notifications(current_user["user_id"], unread_only)
    return [
        Notification(
            id=notif["id"],
            user_id=notif["user_id"],
            type=notif["type"],
            message=notif["message"],
            related_id=notif["related_id"],
            is_read=notif["is_read"],
            created_at=str(notif["created_at"])
        )
        for notif in notifications
    ]


@app.patch("/notifications/{notification_id}/read")
def mark_notification_read_endpoint(
    notification_id: int,
    current_user: dict = Depends(get_current_user)
):
    """Mark a notification as read"""
    mark_notification_as_read(notification_id, current_user["user_id"])
    return {"message": "Notification marked as read"}


@app.patch("/notifications/read-all")
def mark_all_notifications_read_endpoint(
    current_user: dict = Depends(get_current_user)
):
    """Mark all notifications as read"""
    mark_all_notifications_as_read(current_user["user_id"])
    return {"message": "All notifications marked as read"}


@app.delete("/notifications/{notification_id}")
def delete_notification_endpoint(
    notification_id: int,
    current_user: dict = Depends(get_current_user)
):
    """Delete a notification"""
    delete_notification(notification_id, current_user["user_id"])
    return {"message": "Notification deleted"}


# ===== Backup & Restore Endpoints (Admin Only) =====

@app.post("/admin/backup")
def trigger_backup(
    current_user: dict = Depends(get_current_user)
):
    """Trigger a manual database backup (admin only)"""
    if not current_user.get("is_admin", False):
        raise HTTPException(status_code=403, detail="Admin access required")
    
    result = perform_backup()
    if not result['success']:
        raise HTTPException(status_code=500, detail=result['error'])
    
    return result


@app.get("/admin/backups")
def list_backups(
    current_user: dict = Depends(get_current_user)
):
    """List all available backups (admin only)"""
    if not current_user.get("is_admin", False):
        raise HTTPException(status_code=403, detail="Admin access required")
    
    try:
        backups = list_r2_backups()
        environment = get_environment()
        bucket = get_r2_bucket()
        return {
            "backups": backups,
            "environment": environment,
            "bucket": bucket
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@app.post("/admin/restore")
def trigger_restore(
    filename: str,
    current_user: dict = Depends(get_current_user)
):
    """Restore database from a backup (admin only)"""
    if not current_user.get("is_admin", False):
        raise HTTPException(status_code=403, detail="Admin access required")
    
    result = perform_restore(filename)
    if not result['success']:
        raise HTTPException(status_code=500, detail=result['error'])
    
    return result


if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
    