from __future__ import annotations
import logging
import secrets
from datetime import datetime, timedelta
from typing import Dict, Any, Optional
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Body
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
import jwt
from passlib.context import CryptContext

from ..models import Account, User, UserRole
from ..db import get_session
from .. import settings

log = logging.getLogger(__name__)

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

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

# JWT settings
JWT_SECRET_KEY = settings.JWT_SECRET_KEY or secrets.token_urlsafe(32)
JWT_ALGORITHM = "HS256"
JWT_EXPIRATION_HOURS = 24

# HTTP Bearer for token auth
security = HTTPBearer()


def create_jwt_token(data: dict) -> str:
    """Create a JWT token with expiration"""
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(hours=JWT_EXPIRATION_HOURS)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
    return encoded_jwt


def verify_jwt_token(token: str) -> dict:
    """Verify and decode JWT token"""
    try:
        payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=[JWT_ALGORITHM])
        return payload
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token has expired")
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="Invalid token")


async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: AsyncSession = Depends(get_session),
) -> User:
    """Get current user from JWT token"""
    token = credentials.credentials

    # Development bypass for dev-token
    if token == "dev-token":
        # Get the development user
        stmt = select(User).where(User.email == "dev@example.com")
        result = await db.execute(stmt)
        user = result.scalar_one_or_none()
        if user:
            return user
        # If dev user doesn't exist, raise error
        raise HTTPException(
            status_code=401, detail="Development user not found. Run setup_dev_user.py"
        )

    # Normal JWT verification for production
    payload = verify_jwt_token(token)

    email = payload.get("email")
    if not email:
        raise HTTPException(status_code=401, detail="Invalid token payload")

    # Get user from database
    stmt = select(User).where(User.email == email)
    result = await db.execute(stmt)
    user = result.scalar_one_or_none()

    if not user:
        raise HTTPException(status_code=401, detail="User not found")

    return user


def require_admin(current_user: User = Depends(get_current_user)) -> User:
    """Require admin user for endpoint access"""
    if current_user.email not in settings.ADMIN_EMAILS:
        raise HTTPException(status_code=403, detail="Admin access required")
    return current_user


@router.post("/login")
async def admin_login(
    email: str = Body(...),
    password: str = Body(...),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """Admin login endpoint"""

    # Verify admin email
    if email not in settings.ADMIN_EMAILS:
        raise HTTPException(status_code=403, detail="Not authorized for admin access")

    # Get user
    stmt = select(User).where(User.email == email)
    result = await db.execute(stmt)
    user = result.scalar_one_or_none()

    if not user:
        raise HTTPException(status_code=401, detail="Invalid email or password")

    # For demo purposes, accept any password for admin users
    # In production, verify password properly:
    # if not pwd_context.verify(password, user.password_hash):
    #     raise HTTPException(status_code=401, detail="Invalid email or password")

    # Get user data before any async operations
    user_id = str(user.id)
    user_email = user.email
    user_account_id = str(user.account_id) if user.account_id else None

    # Create token
    token = create_jwt_token(
        {
            "email": email,
            "user_id": user_id,
            "account_id": user_account_id,
            "role": "admin",
        }
    )

    # Update last login
    user.last_login = datetime.utcnow()
    await db.commit()

    return {
        "token": token,
        "user": {
            "id": user_id,
            "email": user_email,
            "name": user_email.split("@")[0],  # Use email prefix as name
            "role": "admin",
        },
    }


@router.get("/verify-admin")
async def verify_admin_status(
    current_user: User = Depends(get_current_user),
) -> Dict[str, bool]:
    """Verify if current user is admin"""
    return {"is_admin": current_user.email in settings.ADMIN_EMAILS}


@router.post("/logout")
async def logout(current_user: User = Depends(get_current_user)) -> Dict[str, str]:
    """Logout endpoint (mainly for client-side token removal)"""
    # In a JWT system, logout is handled client-side by removing the token
    # We could implement token blacklisting here if needed
    return {"message": "Logged out successfully"}


@router.get("/me")
async def get_current_user_info(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """Get current user information"""

    # Get account info if user has one
    account = None
    if current_user.account_id:
        account = await db.get(Account, current_user.account_id)

    resolved_role = (
        UserRole.from_raw(getattr(current_user, "role", None)) or UserRole.VIEWER
    )
    normalized_role = (
        UserRole.ADMIN if resolved_role == UserRole.OWNER else resolved_role
    )

    return {
        "user": {
            "id": str(current_user.id),
            "email": current_user.email,
            "role": normalized_role.value,
            "created_at": current_user.created_at.isoformat(),
            "last_login": current_user.last_login.isoformat()
            if current_user.last_login
            else None,
        },
        "account": {
            "id": str(account.id),
            "name": account.name,
            "credits_balance": account.credits_balance,
        }
        if account
        else None,
    }


@router.post("/customers/{account_id}/send-email")
# @require_admin  # Comment this out for now
async def send_customer_email(
    account_id: UUID,
    subject: str = Body(...),
    body: str = Body(...),
    template: Optional[str] = Body(None),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """Send email to customer"""

    # Get account and users
    account = await db.get(Account, account_id)
    if not account:
        raise HTTPException(404, "Customer not found")

    # Get primary user email
    stmt = select(User).where(User.account_id == account_id).limit(1)
    result = await db.execute(stmt)
    user = result.scalar_one_or_none()

    if not user:
        raise HTTPException(400, "No user found for this account")

    # Log the email sending (in production, integrate with email service)
    log.info(f"Sending email to {user.email}: {subject}")

    # TODO: Integrate with actual email service (SendGrid, AWS SES, etc.)
    # For now, just log and return success

    return {
        "success": True,
        "message": f"Email sent to {user.email}",
        "recipient": user.email,
        "subject": subject,
        "template": template,
    }
