from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException, status
from typing import List, Optional

from app.models.user_models import User, UserAddress
from app.schemas.user_schemas import UserCreate, UserUpdate, AddressCreate, AddressUpdate
from app.core import get_password_hash, verify_password


# Функции для работы с пользователями
def get_user(db: Session, user_id: int) -> Optional[User]:
    return db.query(User).filter(User.id == user_id).first()


def get_user_by_email(db: Session, email: str) -> Optional[User]:
    return db.query(User).filter(User.email == email).first()


def get_users(db: Session, skip: int = 0, limit: int = 100) -> List[User]:
    return db.query(User).offset(skip).limit(limit).all()


def create_user(db: Session, user: UserCreate) -> User:
    # Проверяем, что пользователь с таким email не существует
    if get_user_by_email(db, user.email):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Пользователь с таким email уже существует"
        )
    
    # Создаем нового пользователя
    hashed_password = get_password_hash(user.password)
    db_user = User(
        email=user.email,
        password=hashed_password,
        phone=user.phone,
        first_name=user.first_name,
        last_name=user.last_name,
        is_active=user.is_active,
        is_admin=user.is_admin
    )
    
    try:
        db.add(db_user)
        db.commit()
        db.refresh(db_user)
        return db_user
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при создании пользователя"
        )


def update_user(db: Session, user_id: int, user: UserUpdate) -> User:
    db_user = get_user(db, user_id)
    if not db_user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Пользователь не найден"
        )
    
    # Обновляем только предоставленные поля
    update_data = user.dict(exclude_unset=True)
    
    # Если предоставлен новый пароль, хешируем его
    if "password" in update_data and update_data["password"]:
        update_data["password"] = get_password_hash(update_data.pop("password"))
    
    # Удаляем поле password_confirm, если оно есть
    update_data.pop("password_confirm", None)
    
    # Проверяем уникальность email, если он изменяется
    if "email" in update_data and update_data["email"] != db_user.email:
        if get_user_by_email(db, update_data["email"]):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Пользователь с таким email уже существует"
            )
    
    # Применяем обновления
    for key, value in update_data.items():
        setattr(db_user, key, value)
    
    try:
        db.commit()
        db.refresh(db_user)
        return db_user
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при обновлении пользователя"
        )


def delete_user(db: Session, user_id: int) -> bool:
    db_user = get_user(db, user_id)
    if not db_user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Пользователь не найден"
        )
    
    try:
        db.delete(db_user)
        db.commit()
        return True
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при удалении пользователя"
        )


def authenticate_user(db: Session, email: str, password: str) -> Optional[User]:
    user = get_user_by_email(db, email)
    if not user:
        return None
    if not verify_password(password, user.password):
        return None
    return user


# Функции для работы с адресами пользователей
def get_address(db: Session, address_id: int) -> Optional[UserAddress]:
    return db.query(UserAddress).filter(UserAddress.id == address_id).first()


def get_user_addresses(db: Session, user_id: int) -> List[UserAddress]:
    return db.query(UserAddress).filter(UserAddress.user_id == user_id).all()


def create_address(db: Session, address: AddressCreate, user_id: int) -> UserAddress:
    # Если новый адрес помечен как дефолтный, сбрасываем дефолтный статус у других адресов пользователя
    if address.is_default:
        db.query(UserAddress).filter(
            UserAddress.user_id == user_id,
            UserAddress.is_default == True
        ).update({"is_default": False})
    
    db_address = UserAddress(
        user_id=user_id,
        address_line1=address.address_line1,
        address_line2=address.address_line2,
        city=address.city,
        state=address.state,
        postal_code=address.postal_code,
        country=address.country,
        is_default=address.is_default
    )
    
    try:
        db.add(db_address)
        db.commit()
        db.refresh(db_address)
        return db_address
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при создании адреса"
        )


def update_address(db: Session, address_id: int, address: AddressUpdate, user_id: int) -> UserAddress:
    db_address = db.query(UserAddress).filter(
        UserAddress.id == address_id,
        UserAddress.user_id == user_id
    ).first()
    
    if not db_address:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Адрес не найден или не принадлежит пользователю"
        )
    
    # Обновляем только предоставленные поля
    update_data = address.dict(exclude_unset=True)
    
    # Если адрес становится дефолтным, сбрасываем дефолтный статус у других адресов пользователя
    if "is_default" in update_data and update_data["is_default"]:
        db.query(UserAddress).filter(
            UserAddress.user_id == user_id,
            UserAddress.id != address_id,
            UserAddress.is_default == True
        ).update({"is_default": False})
    
    # Применяем обновления
    for key, value in update_data.items():
        setattr(db_address, key, value)
    
    try:
        db.commit()
        db.refresh(db_address)
        return db_address
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при обновлении адреса"
        )


def delete_address(db: Session, address_id: int, user_id: int) -> bool:
    db_address = db.query(UserAddress).filter(
        UserAddress.id == address_id,
        UserAddress.user_id == user_id
    ).first()
    
    if not db_address:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Адрес не найден или не принадлежит пользователю"
        )
    
    try:
        db.delete(db_address)
        db.commit()
        return True
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при удалении адреса"
        )


# Функции для работы с паролями и токенами сброса
def update_password(db: Session, user_id: int, new_password: str) -> bool:
    """Обновляет пароль пользователя"""
    db_user = get_user(db, user_id)
    if not db_user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Пользователь не найден"
        )
    
    hashed_password = get_password_hash(new_password)
    db_user.password = hashed_password
    
    try:
        db.commit()
        return True
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при обновлении пароля"
        )


def create_password_reset_token(db: Session, user_id: int) -> str:
    """Создает токен для сброса пароля"""
    import secrets
    import datetime
    
    # В реальном приложении здесь должна быть модель для токенов сброса пароля
    # Для примера просто генерируем случайный токен
    token = secrets.token_urlsafe(32)
    
    # В реальном приложении сохраняем токен в базе данных с привязкой к пользователю
    # и временем истечения срока действия
    
    return token


def verify_password_reset_token(db: Session, token: str) -> Optional[int]:
    """Проверяет токен сброса пароля и возвращает ID пользователя"""
    # В реальном приложении проверяем токен в базе данных
    # и его срок действия
    
    # Для примера просто возвращаем фиктивный ID пользователя
    # В реальном приложении это должна быть проверка в базе данных
    return 1  # Фиктивный ID пользователя 