from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from . import repository
from .schema import (
    UserCreate, UserUpdate, UserResponse,
    UserprofileCreate, UserprofileUpdate, UserprofileResponse,
    SellerprofileCreate, SellerprofileUpdate, SellerprofileResponse,
    AddressCreate, AddressUpdate, AddressResponse,
    UserProfileDetailsResponse
)
from passlib.context import CryptContext
from datetime import datetime

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


def _get_or_raise(db: Session, entity_id: str, repo_func) -> any:
    entity = repo_func(db, entity_id)
    if not entity:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Resource 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)


def create_user(db: Session, data: UserCreate) -> UserResponse:
    existing_user = repository.get_user_by_email(db, data.email)
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Email already registered"
        )
    
    try:
        user_data = data.model_dump(exclude={"password"})
        user_data["password_hash"] = hash_password(data.password)
        user = repository.create(db, user_data)
        db.commit()
        db.refresh(user)
        return UserResponse.model_validate(user)
    except Exception:
        db.rollback()
        raise


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


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


def update_user(db: Session, user_id: str, data: UserUpdate) -> UserResponse:
    _get_or_raise(db, user_id, repository.get_by_id)
    
    update_data = data.model_dump(exclude_unset=True)
    if "email" in update_data:
        existing_user = repository.get_user_by_email(db, update_data["email"])
        if existing_user and existing_user.id != user_id:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Email already in use by another user"
            )
    
    try:
        user = repository.update(db, user_id, update_data)
        db.commit()
        db.refresh(user)
        return UserResponse.model_validate(user)
    except Exception:
        db.rollback()
        raise


def delete_user(db: Session, user_id: str) -> dict:
    _get_or_raise(db, user_id, repository.get_by_id)
    
    try:
        repository.delete(db, user_id)
        db.commit()
        return {"message": "User deleted successfully"}
    except Exception:
        db.rollback()
        raise


def get_user_profile_details(db: Session, user_id: str) -> UserProfileDetailsResponse:
    user = repository.get_with_details(db, user_id)
    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    return UserProfileDetailsResponse.model_validate(user)


def register_user(db: Session, data: UserCreate) -> UserResponse:
    existing_user = repository.get_user_by_email(db, data.email)
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Email already registered"
        )
    
    try:
        user_data = data.model_dump(exclude={"password"})
        user_data["password_hash"] = hash_password(data.password)
        user_data["is_verified"] = False
        user = repository.create(db, user_data)
        db.commit()
        db.refresh(user)
        return UserResponse.model_validate(user)
    except Exception:
        db.rollback()
        raise


def login_user(db: Session, email: str, password: str) -> UserResponse:
    user = repository.get_user_by_email(db, email)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Invalid email or password"
        )
    
    if not verify_password(password, user.password_hash):
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Invalid email or password"
        )
    
    if not user.is_active:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Account is inactive"
        )
    
    try:
        user.last_login_at = datetime.utcnow()
        db.commit()
        db.refresh(user)
        return UserResponse.model_validate(user)
    except Exception:
        db.rollback()
        raise


def verify_email(db: Session, user_id: str) -> UserResponse:
    user = _get_or_raise(db, user_id, repository.get_by_id)
    
    if user.is_verified:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Email already verified"
        )
    
    try:
        user.is_verified = True
        db.commit()
        db.refresh(user)
        return UserResponse.model_validate(user)
    except Exception:
        db.rollback()
        raise


def become_seller(db: Session, user_id: str, store_name: str, store_description: Optional[str] = None) -> SellerprofileResponse:
    user = _get_or_raise(db, user_id, repository.get_by_id)
    
    if not user.is_verified:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Email must be verified before becoming a seller"
        )
    
    existing_seller = repository.get_sellerprofile_by_user_id(db, user_id)
    if existing_seller:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="User already has a seller profile"
        )
    
    try:
        seller_data = {
            "user_id": user_id,
            "store_name": store_name,
            "store_description": store_description,
            "is_verified_seller": False
        }
        seller_profile = repository.create_sellerprofile(db, seller_data)
        user.role = "SELLER"
        db.commit()
        db.refresh(seller_profile)
        return SellerprofileResponse.model_validate(seller_profile)
    except Exception:
        db.rollback()
        raise


def create_userprofile(db: Session, data: UserprofileCreate) -> UserprofileResponse:
    _get_or_raise(db, data.user_id, repository.get_by_id)
    
    existing_profile = repository.get_userprofile_by_user_id(db, data.user_id)
    if existing_profile:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="User profile already exists for this user"
        )
    
    try:
        userprofile = repository.create_userprofile(db, data.model_dump())
        db.commit()
        db.refresh(userprofile)
        return UserprofileResponse.model_validate(userprofile)
    except Exception:
        db.rollback()
        raise


def get_userprofile(db: Session, userprofile_id: str) -> UserprofileResponse:
    userprofile = _get_or_raise(db, userprofile_id, repository.get_userprofile_by_id)
    return UserprofileResponse.model_validate(userprofile)


def list_userprofiles(db: Session, limit: int = 20, offset: int = 0, user_id: Optional[str] = None) -> List[UserprofileResponse]:
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    
    userprofiles = repository.list_all_userprofiles(db, limit, offset, **filters)
    return [UserprofileResponse.model_validate(up) for up in userprofiles]


def update_userprofile(db: Session, userprofile_id: str, data: UserprofileUpdate) -> UserprofileResponse:
    _get_or_raise(db, userprofile_id, repository.get_userprofile_by_id)
    
    try:
        userprofile = repository.update_userprofile(db, userprofile_id, data.model_dump(exclude_unset=True))
        db.commit()
        db.refresh(userprofile)
        return UserprofileResponse.model_validate(userprofile)
    except Exception:
        db.rollback()
        raise


def delete_userprofile(db: Session, userprofile_id: str) -> dict:
    _get_or_raise(db, userprofile_id, repository.get_userprofile_by_id)
    
    try:
        repository.delete_userprofile(db, userprofile_id)
        db.commit()
        return {"message": "User profile deleted successfully"}
    except Exception:
        db.rollback()
        raise


def create_sellerprofile(db: Session, data: SellerprofileCreate) -> SellerprofileResponse:
    _get_or_raise(db, data.user_id, repository.get_by_id)
    
    existing_profile = repository.get_sellerprofile_by_user_id(db, data.user_id)
    if existing_profile:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Seller profile already exists for this user"
        )
    
    try:
        sellerprofile = repository.create_sellerprofile(db, data.model_dump())
        db.commit()
        db.refresh(sellerprofile)
        return SellerprofileResponse.model_validate(sellerprofile)
    except Exception:
        db.rollback()
        raise


def get_sellerprofile(db: Session, sellerprofile_id: str) -> SellerprofileResponse:
    sellerprofile = _get_or_raise(db, sellerprofile_id, repository.get_sellerprofile_by_id)
    return SellerprofileResponse.model_validate(sellerprofile)


def list_sellerprofiles(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    user_id: Optional[str] = None,
    is_verified_seller: Optional[bool] = None
) -> List[SellerprofileResponse]:
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    if is_verified_seller is not None:
        filters["is_verified_seller"] = is_verified_seller
    
    sellerprofiles = repository.list_all_sellerprofiles(db, limit, offset, **filters)
    return [SellerprofileResponse.model_validate(sp) for sp in sellerprofiles]


def update_sellerprofile(db: Session, sellerprofile_id: str, data: SellerprofileUpdate) -> SellerprofileResponse:
    _get_or_raise(db, sellerprofile_id, repository.get_sellerprofile_by_id)
    
    try:
        sellerprofile = repository.update_sellerprofile(db, sellerprofile_id, data.model_dump(exclude_unset=True))
        db.commit()
        db.refresh(sellerprofile)
        return SellerprofileResponse.model_validate(sellerprofile)
    except Exception:
        db.rollback()
        raise


def delete_sellerprofile(db: Session, sellerprofile_id: str) -> dict:
    _get_or_raise(db, sellerprofile_id, repository.get_sellerprofile_by_id)
    
    try:
        repository.delete_sellerprofile(db, sellerprofile_id)
        db.commit()
        return {"message": "Seller profile deleted successfully"}
    except Exception:
        db.rollback()
        raise


def create_address(db: Session, data: AddressCreate) -> AddressResponse:
    _get_or_raise(db, data.user_id, repository.get_by_id)
    
    try:
        if data.is_default:
            existing_defaults = repository.list_all_addresses(
                db, limit=100, offset=0, user_id=data.user_id, address_type=data.address_type, is_default=True
            )
            for addr in existing_defaults:
                repository.update_address(db, addr.id, {"is_default": False})
        
        address = repository.create_address(db, data.model_dump())
        db.commit()
        db.refresh(address)
        return AddressResponse.model_validate(address)
    except Exception:
        db.rollback()
        raise


def get_address(db: Session, address_id: str) -> AddressResponse:
    address = _get_or_raise(db, address_id, repository.get_address_by_id)
    return AddressResponse.model_validate(address)


def list_addresses(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    user_id: Optional[str] = None,
    address_type: Optional[str] = None,
    is_default: Optional[bool] = None
) -> List[AddressResponse]:
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    if address_type:
        filters["address_type"] = address_type
    if is_default is not None:
        filters["is_default"] = is_default
    
    addresses = repository.list_all_addresses(db, limit, offset, **filters)
    return [AddressResponse.model_validate(addr) for addr in addresses]


def update_address(db: Session, address_id: str, data: AddressUpdate) -> AddressResponse:
    address = _get_or_raise(db, address_id, repository.get_address_by_id)
    
    try:
        update_data = data.model_dump(exclude_unset=True)
        if update_data.get("is_default") is True:
            existing_defaults = repository.list_all_addresses(
                db, limit=100, offset=0, user_id=address.user_id, address_type=address.address_type, is_default=True
            )
            for addr in existing_defaults:
                if addr.id != address_id:
                    repository.update_address(db, addr.id, {"is_default": False})
        
        address = repository.update_address(db, address_id, update_data)
        db.commit()
        db.refresh(address)
        return AddressResponse.model_validate(address)
    except Exception:
        db.rollback()
        raise


def delete_address(db: Session, address_id: str) -> dict:
    _get_or_raise(db, address_id, repository.get_address_by_id)
    
    try:
        repository.delete_address(db, address_id)
        db.commit()
        return {"message": "Address deleted successfully"}
    except Exception:
        db.rollback()
        raise