"""Authentication API routes."""

from datetime import datetime, timezone
from typing import Optional
from fastapi import APIRouter, HTTPException, Response, Depends
from pydantic import BaseModel, EmailStr

from ..config import cfg
from ..db import User, EmailVerification, Session, Token
from .utils import (
    hash_password,
    verify_password,
    generate_token,
    hash_token,
    generate_session_secret,
    get_expiry_time,
)
from .email import send_verification_email
from .dependencies import get_current_user, get_optional_user


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


class RegisterRequest(BaseModel):
    username: str
    email: EmailStr
    password: str


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


class CreateTokenRequest(BaseModel):
    name: str


@router.post("/register")
def register(req: RegisterRequest):
    """Register new user."""

    # Check if username or email already exists
    if User.get_or_none(User.username == req.username):
        raise HTTPException(400, detail="Username already exists")

    if User.get_or_none(User.email == req.email):
        raise HTTPException(400, detail="Email already exists")

    # Create user
    user = User.create(
        username=req.username,
        email=req.email,
        password_hash=hash_password(req.password),
        email_verified=not cfg.auth.require_email_verification,
    )

    # Send verification email if required
    if cfg.auth.require_email_verification:
        token = generate_token()
        EmailVerification.create(
            user=user.id, token=token, expires_at=get_expiry_time(24)
        )

        if not send_verification_email(req.email, req.username, token):
            return {
                "success": True,
                "message": "User created but failed to send verification email",
                "email_verified": False,
            }

        return {
            "success": True,
            "message": "User created. Please check your email to verify your account.",
            "email_verified": False,
        }

    return {
        "success": True,
        "message": "User created successfully",
        "email_verified": True,
    }


@router.get("/verify-email")
def verify_email(token: str):
    """Verify email with token."""

    verification = EmailVerification.get_or_none(
        (EmailVerification.token == token)
        & (EmailVerification.expires_at > datetime.now(timezone.utc))
    )

    if not verification:
        raise HTTPException(400, detail="Invalid or expired verification token")

    # Update user
    User.update(email_verified=True).where(User.id == verification.user).execute()

    # Delete verification token
    EmailVerification.delete().where(EmailVerification.id == verification.id).execute()

    return {"success": True, "message": "Email verified successfully"}


@router.post("/login")
def login(req: LoginRequest, response: Response):
    """Login and create session."""

    user = User.get_or_none(User.username == req.username)

    if not user or not verify_password(req.password, user.password_hash):
        raise HTTPException(401, detail="Invalid username or password")

    if not user.is_active:
        raise HTTPException(403, detail="Account is disabled")

    if cfg.auth.require_email_verification and not user.email_verified:
        raise HTTPException(403, detail="Please verify your email first")

    # Create session
    session_id = generate_token()
    session_secret = generate_session_secret()

    Session.create(
        session_id=session_id,
        user_id=user.id,
        secret=session_secret,
        expires_at=get_expiry_time(cfg.auth.session_expire_hours),
    )

    # Set cookie
    response.set_cookie(
        key="session_id",
        value=session_id,
        httponly=True,
        max_age=cfg.auth.session_expire_hours * 3600,
        samesite="lax",
    )

    return {
        "success": True,
        "message": "Logged in successfully",
        "username": user.username,
        "session_secret": session_secret,
    }


@router.post("/logout")
def logout(response: Response, user: User = Depends(get_current_user)):
    """Logout and destroy session."""

    # Delete all user sessions
    Session.delete().where(Session.user_id == user.id).execute()

    # Clear cookie
    response.delete_cookie(key="session_id")

    return {"success": True, "message": "Logged out successfully"}


@router.get("/me")
def get_me(user: User = Depends(get_current_user)):
    """Get current user info (internal endpoint)."""

    return {
        "id": user.id,
        "username": user.username,
        "email": user.email,
        "email_verified": user.email_verified,
        "created_at": user.created_at.isoformat(),
    }


@router.get("/tokens")
def list_tokens(user: User = Depends(get_current_user)):
    """List user's API tokens."""

    tokens = Token.select().where(Token.user_id == user.id)

    return {
        "tokens": [
            {
                "id": t.id,
                "name": t.name,
                "last_used": t.last_used.isoformat() if t.last_used else None,
                "created_at": t.created_at.isoformat(),
            }
            for t in tokens
        ]
    }


@router.post("/tokens/create")
def create_token(req: CreateTokenRequest, user: User = Depends(get_current_user)):
    """Create new API token."""

    # Generate token
    token_str = generate_token()
    token_hash_val = hash_token(token_str)

    # Save to database
    token = Token.create(user_id=user.id, token_hash=token_hash_val, name=req.name)

    # Get session secret for encryption (if in web session)
    session = Session.get_or_none(Session.user_id == user.id)
    session_secret = session.secret if session else None

    return {
        "success": True,
        "token": token_str,
        "token_id": token.id,
        "session_secret": session_secret,
        "message": "Token created. Save it securely - you won't see it again!",
    }


@router.delete("/tokens/{token_id}")
def revoke_token(token_id: int, user: User = Depends(get_current_user)):
    """Revoke an API token."""

    token = Token.get_or_none((Token.id == token_id) & (Token.user_id == user.id))

    if not token:
        raise HTTPException(404, detail="Token not found")

    token.delete_instance()

    return {"success": True, "message": "Token revoked successfully"}
