from sqlalchemy.orm import Session
from fastapi import  HTTPException, status, Request
from models.user_model import User
from schema.user_schema import CreateUserSchema,UserLoginSchema, ForgotPasswordRequestSchema, ResetPasswordRequestSchema,UpdateProfileByOrgSchema,UpdateProfileSchema
from utils.logging import logger
from utils.JWTToken import create_access_token, create_reset_password_token, decode_reset_password_token
from sqlalchemy.exc import SQLAlchemyError
from utils.hashing_password import hash_password, verify_password
from utils.send_reset_email import send_reset_password_email
from uuid import UUID
import os
from utils.pagination import get_paginated_response

FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:3000")
def all_users_from_db(db:Session, request: Request, page: int = 1, limit: int = 10, search: str = None, platform_role: str = None):
    try:
        logger.info(f"Fetching user info from db.")
        query = db.query(User)
        
        if search:
            search_query = f"%{search}%"
            query = query.filter(
                (User.name.ilike(search_query)) | 
                (User.email.ilike(search_query))
            )
            
        if platform_role:
            query = query.filter(User.platform_role == platform_role)
            
        paginated_data = get_paginated_response(
            query=query,
            page=page,
            limit=limit,
            request=request
        )
        return paginated_data
    except Exception as e:
        logger.error(f"Error while fetching data: {str(e)}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,detail=f"Error is {e}")

def create_new_user(request: CreateUserSchema, db: Session):
    try:

        existing_user = (
            db.query(User)
            .filter(User.email == request.email)
            .first()
        )

        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="User already exists!"
            )
        hashed_password = hash_password(request.password)
        creating_user = User(
            email=request.email,
            name=request.name,
            password=hashed_password,
            platform_role=request.platform_role if request.platform_role else "USER",
            org_role=request.org_role
        )

        db.add(creating_user)
        db.commit()
        db.refresh(creating_user)

        token = create_access_token(creating_user.id)

        return {
            "message": "Account created successfully!",
            "token": token,
            "user": creating_user
        }

    except HTTPException:
        raise

    except SQLAlchemyError as e:
        db.rollback()

        logger.error(f"Database error while creating user: {str(e)}")

        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Database error occurred"
        )

    except Exception as e:
        db.rollback()

        logger.error(f"Unexpected error while creating user: {str(e)}")

        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )
    

def user_login(request: UserLoginSchema, db: Session):

    try:

        logger.info(
            f"Login attempt for email: {request.email}"
        )

        check_if_user_exists = (
            db.query(User)
            .filter(User.email == request.email)
            .first()
        )

        if not check_if_user_exists:

            logger.warning(
                f"User not found with email: {request.email}"
            )

            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found with this email"
            )
        
        if check_if_user_exists.is_active==False:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="User account is inactive"
            )

        logger.info(
            f"User found: {check_if_user_exists.email}"
        )

        is_valid_password = verify_password(
            request.password,
            check_if_user_exists.password
        )

        if not is_valid_password:

            logger.warning(
                f"Invalid password for email: {request.email}"
            )

            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid password"
            )

        logger.info(
            f"Password verified for user: {request.email}"
        )

        token = create_access_token(
            check_if_user_exists.id
        )

        logger.info(
            f"Login successful for user: {request.email}"
        )

        return {
            "message": "Login successful!",
            "token": token,
            "user": check_if_user_exists
        }

    except HTTPException:
        raise

    except Exception as e:

        logger.error(
            f"Error while user login: {str(e)}"
        )

        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )

def get_user_by_id(user_id: str, db: Session):
    try:
        user = db.query(User).filter(User.id == user_id, User.is_active==True).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
        return user
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error while fetching user by ID: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )
    

async def forgot_password(payload: ForgotPasswordRequestSchema, db: Session):
    try:
        user = db.query(User).filter(User.email == payload.email).first()
        if not user:
            # We don't want to reveal if the email exists or not to prevent user enumeration
            # We just return success anyway
            return {"message": "If an account with that email exists, we have sent a password reset link."}
        
        # Generate token
        token = create_reset_password_token(user.id)
        reset_link = f"{FRONTEND_URL}/reset-password?token={token}"
        
        # Send email
        await send_reset_password_email(
            email=user.email,
            reset_link=reset_link,
            user_name=user.name
        )
        
        return {"message": "If an account with that email exists, we have sent a password reset link."}
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error in forgot_password: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )


def reset_password(payload: ResetPasswordRequestSchema, db: Session):
    try:
        # Decode and verify the token
        try:
            decoded = decode_reset_password_token(payload.token)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid or expired reset token"
            )
            
        if decoded.get("type") != "reset_password":
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid token type"
            )
            
        user_id = decoded.get("user_id")
        user = db.query(User).filter(User.id == user_id).first()
        
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
            
        # Hash new password
        user.password = hash_password(payload.new_password)
        db.commit()
        db.refresh(user)
        
        return {"message": "Password reset successfully. You can now login with your new password."}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error in reset_password: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )



def change_user_password(request: Request,user_id:str, db: Session):
    try:
        logger.info(f"Initiating password change for user_id: {request.new_password}")
        if not request.old_password or not request.new_password:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Old password and new password are required"
            )

        if request.old_password == request.new_password:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="New password cannot be the same as old password"
            )
        
        logger.info(f"Fetching user details for password change, user_id: {user_id}")
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
        if not verify_password(request.old_password, user.password):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Old password is incorrect"
            )
        user.password = hash_password(request.new_password)
        db.commit()
        db.refresh(user)
        return {
            "message": "Password changed successfully",
            "user": user
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error while fetching user by ID: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )
    
def update_profile(payload:UpdateProfileSchema,user_id:str, db: Session):
    try:
        if not payload.name and payload.is_active is None:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="At least one field (name or is_active) must be provided for update"
            )
        
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
        user.name = payload.name if payload.name else user.name
        user.is_active = payload.is_active if payload.is_active is not None else user.is_active
        db.commit()
        db.refresh(user)
        return {
            "message": "Profile updated successfully",
            "user": user
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error while updating user profile: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )
    
def update_profile_by_org_admin(payload:UpdateProfileByOrgSchema,request: Request, db: Session):
    try:
        user=request.state.user
        if not user.org_id or user.org_role not in ['ORG_ADMIN', 'MANAGER']:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Only organization admins and managers can update user status")


        if not payload.email or payload.is_active is None:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="At least one field (is_active) must be provided for update"
            )
        
        user = db.query(User).filter(User.email == payload.email).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
        user.is_active = payload.is_active if payload.is_active is not None else user.is_active
        db.commit()
        db.refresh(user)
        return {
            "message": "User Status updated successfully",
            "user": user
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error while updating user profile: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )


def delete_user_by_id(request: Request, db: Session):
    try:
        user_id = request.state.user.id
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
        db.delete(user)
        db.commit()
        return {
            "message": "User deleted successfully"
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error while deleting user: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal server error"
        )

def get_org_members_from_db(org_id: UUID, db: Session):
    try:
        logger.info(f"Fetching members for organization ID: {org_id}")
        members = db.query(User).filter(User.org_id == org_id).all()
        return members
    except Exception as e:
        logger.error(f"Error fetching org members: {str(e)}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))