from datetime import datetime
from typing import Optional

from fastapi import APIRouter, Cookie, Depends, HTTPException, Request, Response
from sqlmodel import Session, SQLModel, select

from app.auth import create_session, hash_password, require_auth, verify_password
from app.database import get_session
from app.models import Session as SessionModel
from app.models import User

router = APIRouter(prefix="/auth", tags=["auth"])


# Request/Response models
class RegisterRequest(SQLModel):
    username: str
    email: str
    password: str
    salt: str
    wrappedMasterKey: str


class LoginRequest(SQLModel):
    username: str
    password: str


class UserResponse(SQLModel):
    id: int
    username: str
    email: str
    salt: str  # Client needs this for key derivation
    wrapped_master_key: str  # Client needs this to unwrap the master key


@router.post("/register")
def register(
    data: RegisterRequest,
    request: Request,
    response: Response,
    db: Session = Depends(get_session),
):
    # Check existing user
    existing = db.exec(
        select(User).where(
            (User.username == data.username) | (User.email == data.email)
        )
    ).first()

    if existing:
        raise HTTPException(status_code=400, detail="User already exists")

    # Create user
    user = User(
        username=data.username,
        email=data.email,
        hashed_password=hash_password(data.password),
        salt=data.salt,
        wrapped_master_key=data.wrappedMasterKey,
    )
    db.add(user)
    db.commit()
    db.refresh(user)

    # Create session
    assert user.id is not None
    session_id = create_session(user.id, request, db)

    # Set cookie
    response.set_cookie(
        key="session_id",
        value=session_id,
        httponly=True,
        secure=True,  # HTTPS only in production
        samesite="lax",
        max_age=30 * 24 * 60 * 60,  # 30 days
    )

    return {"user": UserResponse.model_validate(user)}


@router.post("/login")
def login(
    data: LoginRequest,
    request: Request,
    response: Response,
    db: Session = Depends(get_session),
):
    # Find user
    user = db.exec(select(User).where(User.username == data.username)).first()

    if not user or not verify_password(data.password, user.hashed_password):
        raise HTTPException(status_code=401, detail="Invalid credentials")

    # Create session
    assert user.id is not None
    session_id = create_session(user.id, request, db)

    # Set cookie
    response.set_cookie(
        key="session_id",
        value=session_id,
        httponly=True,
        secure=True,
        samesite="lax",
        max_age=30 * 24 * 60 * 60,
    )

    return {"user": UserResponse.model_validate(user)}


@router.post("/logout")
def logout(
    response: Response,
    session_id: Optional[str] = Cookie(None),
    db: Session = Depends(get_session),
):
    # Delete session from database
    if session_id:
        session = db.exec(
            select(SessionModel).where(SessionModel.session_id == session_id)
        ).first()
        if session:
            db.delete(session)
            db.commit()

    # Clear cookie
    response.delete_cookie("session_id")
    return {"message": "Logged out"}


@router.get("/me")
def get_current_user(current_user: User = Depends(require_auth)):
    return {"user": UserResponse.from_orm(current_user)}


@router.get("/sessions")
def list_sessions(
    current_user: User = Depends(require_auth), db: Session = Depends(get_session)
):
    sessions = db.exec(
        select(SessionModel)
        .where(SessionModel.user_id == current_user.id)
        .where(SessionModel.expires_at > datetime.utcnow())
    ).all()
    return {"sessions": sessions}


@router.delete("/sessions/{session_token}")  # Renamed from session_id
def revoke_session(
    session_token: str,  # Renamed to avoid conflict with Cookie parameter
    current_user: User = Depends(require_auth),
    db: Session = Depends(get_session),
):
    session = db.exec(
        select(SessionModel)
        .where(SessionModel.session_id == session_token)  # Use renamed variable
        .where(SessionModel.user_id == current_user.id)
    ).first()

    if session:
        db.delete(session)
        db.commit()

    return {"message": "Session revoked"}
