from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import List, Optional
from datetime import datetime
from passlib.context import CryptContext
from . import repository
from .schema import (
    UserCreate, UserUpdate, UserResponse,
    UseractivityCreate, UseractivityUpdate, UseractivityResponse,
    UserLogin, UserRegister, LoginResponse, UseractivityWithStock
)

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


def _get_or_raise(db: Session, entity_id: str, repo_module) -> any:
    entity = repo_module.get_by_id(db, entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail=f"Entity with id {entity_id} not found")
    return entity


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)


# User handlers
def create_user(db: Session, data: UserCreate) -> UserResponse:
    try:
        # Check if email already exists
        existing_user = repository.get_by_email(db, data.email)
        if existing_user:
            raise HTTPException(
                status_code=409,
                detail="A user with this email address already exists"
            )
        
        # Hash password
        user_data = data.model_dump(exclude={"password"})
        user_data["password_hash"] = hash_password(data.password)
        user_data["registration_date"] = datetime.utcnow()
        user_data["email"] = data.email.lower()
        
        user = repository.create(db, user_data)
        db.commit()
        db.refresh(user)
        return UserResponse.model_validate(user)
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def list_users(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    role: Optional[str] = None,
    status: Optional[str] = None,
    search: Optional[str] = None
) -> List[UserResponse]:
    filters = {}
    if role:
        filters["role"] = role
    if status:
        filters["status"] = status
    if search:
        filters["search"] = search
    
    users = repository.list_all(db, limit, offset, **filters)
    return [UserResponse.model_validate(user) for user in users]


def get_user(db: Session, user_id: str) -> UserResponse:
    user = _get_or_raise(db, user_id, repository)
    return UserResponse.model_validate(user)


def update_user(db: Session, user_id: str, data: UserUpdate) -> UserResponse:
    try:
        user = _get_or_raise(db, user_id, repository)
        
        update_data = data.model_dump(exclude_unset=True, exclude={"password"})
        
        # Check email uniqueness if email is being updated
        if "email" in update_data:
            update_data["email"] = update_data["email"].lower()
            existing_user = repository.get_by_email(db, update_data["email"])
            if existing_user and existing_user.id != user_id:
                raise HTTPException(
                    status_code=409,
                    detail="A user with this email address already exists"
                )
        
        # Handle password update separately
        if data.password:
            update_data["password_hash"] = hash_password(data.password)
        
        updated_user = repository.update(db, user_id, update_data)
        db.commit()
        db.refresh(updated_user)
        return UserResponse.model_validate(updated_user)
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def delete_user(db: Session, user_id: str) -> dict:
    try:
        user = _get_or_raise(db, user_id, repository)
        
        # Prevent deletion of administrators
        if user.role == "ADMINISTRATOR":
            raise HTTPException(
                status_code=400,
                detail="Administrator accounts cannot be deleted"
            )
        
        repository.delete(db, user_id)
        db.commit()
        return {"message": "User deleted successfully"}
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


# Authentication handlers
def register_user(db: Session, data: UserRegister) -> UserResponse:
    return create_user(db, data)


def login_user(db: Session, data: UserLogin) -> dict:
    try:
        user = repository.get_by_email(db, data.email)
        if not user:
            raise HTTPException(
                status_code=400,
                detail="Invalid email or password"
            )
        
        if not verify_password(data.password, user.password_hash):
            raise HTTPException(
                status_code=400,
                detail="Invalid email or password"
            )
        
        if user.status == "SUSPENDED":
            raise HTTPException(
                status_code=400,
                detail="Your account has been suspended. Please contact support."
            )
        
        if user.status == "INACTIVE":
            raise HTTPException(
                status_code=400,
                detail="Your account is inactive. Please contact support to reactivate."
            )
        
        # Update last login date
        repository.update_last_login(db, user.id)
        db.commit()
        db.refresh(user)
        
        # In a real application, generate JWT token here
        # For now, return a placeholder token
        return {
            "access_token": f"token_{user.id}",
            "token_type": "bearer",
            "user": UserResponse.model_validate(user)
        }
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def logout_user(db: Session, user_id: str) -> dict:
    user = _get_or_raise(db, user_id, repository)
    # In a real application, invalidate JWT token here
    return {"message": "Logged out successfully"}


# UserActivity handlers
def create_user_activity(db: Session, data: UseractivityCreate) -> UseractivityResponse:
    try:
        from stock_management import repository as stock_repo
        
        # Validate user exists
        user = _get_or_raise(db, data.user_id, repository)
        
        # Validate stock exists
        stock = stock_repo.get_by_id(db, data.stock_id)
        if not stock:
            raise HTTPException(
                status_code=404,
                detail=f"Stock with id {data.stock_id} not found"
            )
        
        activity_data = data.model_dump()
        if not activity_data.get("timestamp"):
            activity_data["timestamp"] = datetime.utcnow()
        
        activity = repository.create_activity(db, activity_data)
        db.commit()
        db.refresh(activity)
        return UseractivityResponse.model_validate(activity)
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def list_user_activities(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    user_id: Optional[str] = None,
    stock_id: Optional[str] = None,
    activity_type: Optional[str] = None,
    session_id: Optional[str] = None
) -> List[UseractivityResponse]:
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    if stock_id:
        filters["stock_id"] = stock_id
    if activity_type:
        filters["activity_type"] = activity_type
    if session_id:
        filters["session_id"] = session_id
    
    activities = repository.list_all_activities(db, limit, offset, **filters)
    return [UseractivityResponse.model_validate(activity) for activity in activities]


def get_user_activity(db: Session, activity_id: str) -> UseractivityResponse:
    activity = repository.get_activity_by_id(db, activity_id)
    if not activity:
        raise HTTPException(
            status_code=404,
            detail=f"User activity with id {activity_id} not found"
        )
    return UseractivityResponse.model_validate(activity)


def update_user_activity(db: Session, activity_id: str, data: UseractivityUpdate) -> UseractivityResponse:
    try:
        activity = repository.get_activity_by_id(db, activity_id)
        if not activity:
            raise HTTPException(
                status_code=404,
                detail=f"User activity with id {activity_id} not found"
            )
        
        update_data = data.model_dump(exclude_unset=True)
        
        # Validate user if being updated
        if "user_id" in update_data:
            user = _get_or_raise(db, update_data["user_id"], repository)
        
        # Validate stock if being updated
        if "stock_id" in update_data:
            from stock_management import repository as stock_repo
            stock = stock_repo.get_by_id(db, update_data["stock_id"])
            if not stock:
                raise HTTPException(
                    status_code=404,
                    detail=f"Stock with id {update_data['stock_id']} not found"
                )
        
        updated_activity = repository.update_activity(db, activity_id, update_data)
        db.commit()
        db.refresh(updated_activity)
        return UseractivityResponse.model_validate(updated_activity)
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def delete_user_activity(db: Session, activity_id: str) -> dict:
    try:
        activity = repository.get_activity_by_id(db, activity_id)
        if not activity:
            raise HTTPException(
                status_code=404,
                detail=f"User activity with id {activity_id} not found"
            )
        
        repository.delete_activity(db, activity_id)
        db.commit()
        return {"message": "User activity deleted successfully"}
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def get_user_activities_with_stock_details(
    db: Session,
    user_id: str,
    limit: int = 20,
    offset: int = 0
) -> List[UseractivityWithStock]:
    user = _get_or_raise(db, user_id, repository)
    
    activities = repository.get_user_activities_with_stock(db, user_id, limit, offset)
    return [UseractivityWithStock.model_validate(activity) for activity in activities]


def log_user_activity(
    db: Session,
    user_id: str,
    stock_id: str,
    activity_type: str,
    session_id: Optional[str] = None
) -> UseractivityResponse:
    activity_data = UseractivityCreate(
        user_id=user_id,
        stock_id=stock_id,
        activity_type=activity_type,
        session_id=session_id
    )
    return create_user_activity(db, activity_data)