import hashlib
import hmac
import secrets
from datetime import datetime, timedelta, timezone

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.core.config import settings
from app.core.security import hash_password
from app.models.password_reset import PasswordReset
from app.models.user import User


PASSWORD_RESET_EXPIRE_MINUTES = 15


def _hash_reset_token(token: str) -> str:
    return hmac.new(
        settings.JWT_SECRET.encode(),
        token.encode(),
        hashlib.sha256,
    ).hexdigest()


def create_password_reset(
    db: Session,
    user: User,
) -> str:
    now = datetime.now(timezone.utc)

    existing_resets = db.scalars(
        select(PasswordReset).where(
            PasswordReset.user_id == user.id,
            PasswordReset.used_at.is_(None),
        )
    ).all()

    for reset in existing_resets:
        reset.used_at = now

    raw_token = secrets.token_urlsafe(32)

    reset = PasswordReset(
        user_id=user.id,
        token_hash=_hash_reset_token(raw_token),
        expires_at=now
        + timedelta(minutes=PASSWORD_RESET_EXPIRE_MINUTES),
    )

    db.add(reset)
    db.commit()

    return raw_token


def reset_password_with_token(
    db: Session,
    user: User,
    token: str,
    new_password: str,
) -> bool:
    now = datetime.now(timezone.utc)

    reset = db.scalar(
        select(PasswordReset)
        .where(
            PasswordReset.user_id == user.id,
            PasswordReset.used_at.is_(None),
        )
        .order_by(PasswordReset.created_at.desc())
    )

    if not reset:
        return False

    if reset.expires_at < now:
        reset.used_at = now
        db.commit()
        return False

    expected_hash = _hash_reset_token(token)

    if not hmac.compare_digest(
        reset.token_hash,
        expected_hash,
    ):
        return False

    user.password_hash = hash_password(new_password)
    reset.used_at = now

    db.commit()
    db.refresh(user)

    return True