"""
User Services for handling user-related business logic.
"""

from typing import Dict, List, Optional, Tuple, Union
from datetime import datetime, time
import uuid
import logging
from fastapi import HTTPException, status
from sqlalchemy import select, and_, or_, func, desc, asc
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from pydantic.networks import EmailStr

from src.apps.users.models.user import User
from src.apps.users.schemas.user_common import UserResponseSchema, UserCreateSchema, UserUpdateSchema
from src.core.utils.password import encrypt_password, verify_password
from src.apps.base.utils.functions import generate_secure_id
from src.helpers.pagination import QueryPaginator

logger = logging.getLogger(__name__)


def get_user(db: Session, user_id: int) -> Optional[Tuple[UserResponseSchema, str]]:
    """Get user by ID."""
    try:
        stmt = select(User).where(User.id == user_id)
        if hasattr(User, 'deleted_at'):
            stmt = stmt.where(User.deleted_at.is_(None))
        
        result = db.execute(stmt).scalar_one_or_none()
        if result:
            return (UserResponseSchema.model_validate(result), "User found")
        return (None, "User not found")
    except Exception as e:
        logger.error(f"Error getting user: {e}")
        return (None, str(e))


def get_user_raw(db: Session, user_id: int) -> Optional[User]:
    """Get raw user model by ID."""
    try:
        stmt = select(User).where(User.id == user_id)
        if hasattr(User, 'deleted_at'):
            stmt = stmt.where(User.deleted_at.is_(None))
        
        return db.execute(stmt).scalar_one_or_none()
    except Exception as e:
        logger.error(f"Error getting raw user: {e}")
        return None


def get_user_by_email(db: Session, email: EmailStr) -> Optional[UserResponseSchema]:
    """Get user by email."""
    try:
        stmt = select(User).where(User.email == email)
        if hasattr(User, 'deleted_at'):
            stmt = stmt.where(User.deleted_at.is_(None))
        
        result = db.execute(stmt).scalar_one_or_none()
        if result:
            return UserResponseSchema.model_validate(result)
        return None
    except Exception as e:
        logger.error(f"Error getting user by email: {e}")
        return None


def get_users_list(
    db: Session,
    search: Optional[str] = None,
    name: Optional[str] = None,
    email: Optional[str] = None,
    phone: Optional[str] = None,
    is_active: int = 0,
    is_verified: int = 0,
    sort_by: List[str] = ["-created_at"],
    skip: int = 0,
    limit: int = 20,
    paginate: bool = True,
) -> Union[List[User], Dict]:
    """Get users list with filtering and pagination."""
    try:
        # Build base query
        stmt = select(User)
        if hasattr(User, 'deleted_at'):
            stmt = stmt.where(User.deleted_at.is_(None))
        
        # Apply search filters
        if search:
            conditions = []
            if hasattr(User, 'first_name'):
                conditions.append(User.first_name.ilike(f"%{search}%"))
            if hasattr(User, 'last_name'):
                conditions.append(User.last_name.ilike(f"%{search}%"))
            
            conditions.append(User.email.ilike(f"%{search}%"))
            
            phone_field = getattr(User, 'phone', None) or getattr(User, 'phone_number', None)
            if phone_field is not None:
                conditions.append(phone_field.like(f"%{search}%"))
            
            if hasattr(User, 'first_name') and hasattr(User, 'last_name'):
                conditions.append(
                    func.concat(User.first_name, " ", User.last_name).ilike(f"%{search}%")
                )
            
            if conditions:
                stmt = stmt.where(or_(*conditions))
        
        # Apply name filters
        if name:
            name_conditions = []
            if hasattr(User, 'first_name'):
                name_conditions.append(User.first_name.ilike(f"%{name}%"))
            if hasattr(User, 'last_name'):
                name_conditions.append(User.last_name.ilike(f"%{name}%"))
            if hasattr(User, 'first_name') and hasattr(User, 'last_name'):
                name_conditions.append(
                    func.concat(User.first_name, " ", User.last_name).ilike(f"%{name}%")
                )
            
            if name_conditions:
                stmt = stmt.where(or_(*name_conditions))
        
        # Apply basic filters
        if email:
            stmt = stmt.where(User.email.ilike(f"%{email}%"))
        
        if phone:
            phone_field = getattr(User, 'phone', None) or getattr(User, 'phone_number', None)
            if phone_field is not None:
                stmt = stmt.where(phone_field.like(f"%{phone}%"))
        
        if is_active != 0:
            stmt = stmt.where(User.is_active == (is_active > 0))
        
        if is_verified != 0:
            stmt = stmt.where(User.is_verified == (is_verified > 0))
        
        # Apply sorting
        if sort_by:
            order_clauses = []
            for sort_field in sort_by:
                col_name = sort_field.replace("-", "")
                
                if hasattr(User, col_name):
                    sort_column = getattr(User, col_name)
                    if sort_field.startswith("-"):
                        order_clauses.append(desc(sort_column))
                    else:
                        order_clauses.append(asc(sort_column))
            
            if order_clauses:
                stmt = stmt.order_by(*order_clauses)
        
        # Execute query
        if paginate:
            paginator = QueryPaginator(
                query=stmt,
                schema=UserResponseSchema,
                db=db,
                model=User,
                url="/api/v1/users",
                offset=skip,
                limit=limit,
                use_orm=True,
            )
            
            pagination_result = paginator.paginate()
            
            return {
                "data": pagination_result.result,
                "total": pagination_result.total,
                "page": pagination_result.page,
                "per_page": pagination_result.per_page,
                "next": pagination_result.next,
                "previous": pagination_result.previous,
                "first": pagination_result.first,
                "last": pagination_result.last,
            }
        else:
            results = db.execute(stmt).scalars().all()
            return results
    
    except Exception as e:
        logger.error(f"Error getting users list: {e}")
        return {"error": str(e)}


def create_user(
    db: Session, 
    user_data: UserCreateSchema,
    password: Optional[str] = None,
    merchant=None,
    customer=None,
    is_owner: bool = False,
    is_customer: bool = False,
    identifier_type: str = "email"
) -> Tuple[Union[UserResponseSchema, User, None], str]:
    """Create a new user."""
    try:
        # Use password from schema if not provided as parameter
        password_to_use = password or user_data.password
        
        if not password_to_use:
            return (None, "Password is required")
        
        # Check for existing user by email
        email_stmt = select(User).where(
            and_(
                User.email == user_data.email,
                User.deleted_at.is_(None) if hasattr(User, 'deleted_at') else True
            )
        )
        existing_email_user = db.execute(email_stmt).scalar_one_or_none()
        if existing_email_user:
            return (None, "User with this email already exists")
        
        # Check for existing user by username
        username_stmt = select(User).where(
            and_(
                User.username == user_data.username,
                User.deleted_at.is_(None) if hasattr(User, 'deleted_at') else True
            )
        )
        existing_username_user = db.execute(username_stmt).scalar_one_or_none()
        if existing_username_user:
            return (None, "User with this username already exists")

        # Check by phone if identifier_type is phone and phone is provided
        if identifier_type == "phone" and hasattr(user_data, 'phone') and user_data.phone:
            phone_field = getattr(User, 'phone', None) or getattr(User, 'phone_number', None)
            if phone_field is not None:
                phone_stmt = select(User).where(
                    and_(
                        phone_field == user_data.phone,
                        User.deleted_at.is_(None) if hasattr(User, 'deleted_at') else True
                    )
                )
                existing_phone_user = db.execute(phone_stmt).scalar_one_or_none()
                if existing_phone_user:
                    return (None, "User with this phone number already exists")

        # Create User model instance with all required fields
        user_obj = User(
            email=user_data.email,
            username=user_data.username,
            first_name=user_data.first_name,
            middle_name=user_data.middle_name,
            last_name=user_data.last_name,
            phone=user_data.phone,
            is_active=user_data.is_active,
            is_verified=user_data.is_verified,
        )
        
        # Hash and set password
        encrypted_password = encrypt_password(password_to_use)
        if encrypted_password:
            user_obj.hashed_password = encrypted_password.decode('utf-8')
        else:
            return (None, "Failed to encrypt password")
        
        # Update full_name field
        if hasattr(user_obj, 'update_full_name'):
            user_obj.update_full_name()
        
        # Add user_id if it doesn't exist
        if not hasattr(user_obj, 'user_id') or user_obj.user_id is None:
            try:
                user_obj.user_id = generate_secure_id(prepend="usr", length=20)
            except:
                user_obj.user_id = f"usr_{str(uuid.uuid4())[:20]}"
        
        db.add(user_obj)
        db.commit()
        db.refresh(user_obj)

        # TODO: Add merchant and customer associations when models are available
        
        return (UserResponseSchema.model_validate(user_obj), "User created successfully")
        
    except IntegrityError as e:
        db.rollback()
        if "email" in str(e.orig):
            err_msg = "User with this email already exists"
        elif "username" in str(e.orig):
            err_msg = "User with this username already exists"
        else:
            err_msg = "Duplicate user data"
        return (None, err_msg)
    except Exception as e:
        db.rollback()
        logger.error(f"Error creating user: {e}")
        return (None, f"Password encryption failed: {str(e)}")


def update_user(
    db: Session, 
    user_id: int, 
    user_data: UserUpdateSchema
) -> Optional[UserResponseSchema]:
    """Update user."""
    try:
        stmt = select(User).where(User.id == user_id)
        if hasattr(User, 'deleted_at'):
            stmt = stmt.where(User.deleted_at.is_(None))
        
        user = db.execute(stmt).scalar_one_or_none()
        if not user:
            return None
        
        # Update fields from schema
        update_data = user_data.model_dump(exclude_unset=True)
        for field, value in update_data.items():
            if hasattr(user, field):
                setattr(user, field, value)
        
        # Update full_name if name fields changed
        if hasattr(user, 'update_full_name') and any(field in update_data for field in ['first_name', 'middle_name', 'last_name']):
            user.update_full_name()
        
        db.commit()
        db.refresh(user)
        
        return UserResponseSchema.model_validate(user)
    
    except Exception as e:
        db.rollback()
        logger.error(f"Error updating user: {e}")
        return None


def delete_user(db: Session, user_id: int) -> Optional[UserResponseSchema]:
    """Hard delete user."""
    try:
        stmt = select(User).where(User.id == user_id)
        if hasattr(User, 'deleted_at'):
            stmt = stmt.where(User.deleted_at.is_(None))
        
        user = db.execute(stmt).scalar_one_or_none()
        if not user:
            return None
        
        user_response = UserResponseSchema.model_validate(user)
        db.delete(user)
        db.commit()
        
        return user_response
    
    except Exception as e:
        db.rollback()
        logger.error(f"Error deleting user: {e}")
        return None


def soft_delete_user(db: Session, user_id: int) -> Optional[Dict]:
    """Soft delete user."""
    try:
        stmt = select(User).where(User.id == user_id)
        if hasattr(User, 'deleted_at'):
            stmt = stmt.where(User.deleted_at.is_(None))
        
        user = db.execute(stmt).scalar_one_or_none()
        if not user:
            return None
        
        if hasattr(user, 'deleted_at'):
            user.deleted_at = datetime.utcnow()
            db.commit()
            
            return {
                "message": "User soft deleted successfully",
                "user_id": user_id,
                "deleted_at": user.deleted_at
            }
        else:
            # If soft delete not supported, perform hard delete
            return delete_user(db, user_id)
    
    except Exception as e:
        db.rollback()
        logger.error(f"Error soft deleting user: {e}")
        return None



