import logging
import secrets
import string
from datetime import datetime, timedelta, timezone

from sqlalchemy.orm import Session

from app.config import get_settings
from app.models import PasswordResetToken, User, UserRole
from app.security import hash_password

logger = logging.getLogger(__name__)


def generate_otp(length: int = 6) -> str:
    alphabet = string.digits
    return "".join(secrets.choice(alphabet) for _ in range(length))


async def send_otp_email(to_email: str, otp: str) -> None:
    settings = get_settings()
    if not settings.smtp_host:
        logger.warning("SMTP not configured — password reset OTP for %s: %s", to_email, otp)
        return

    import aiosmtplib
    from aiosmtplib.errors import SMTPAuthenticationError, SMTPException
    from email.message import EmailMessage

    msg = EmailMessage()
    msg["Subject"] = "Password reset OTP"
    msg["From"] = settings.smtp_from
    msg["To"] = to_email
    msg.set_content(f"Your OTP is: {otp}\nIt expires in 15 minutes.")

    try:
        await aiosmtplib.send(
            msg,
            hostname=settings.smtp_host,
            port=settings.smtp_port,
            username=settings.smtp_user or None,
            password=settings.smtp_password or None,
            start_tls=settings.smtp_tls,
        )
    except SMTPAuthenticationError as exc:
        # Wrong SMTP credentials — no stack trace; OTP still logged for testing.
        logger.warning(
            "SMTP server rejected login (check SMTP_USER / SMTP_PASSWORD in .env): %s",
            exc,
        )
        logger.warning(
            "Password reset OTP for recipient %s (email not sent): %s",
            to_email,
            otp,
        )
    except (SMTPException, OSError) as exc:
        logger.exception("SMTP send failed for %s", to_email)
        logger.warning("Password reset OTP for %s (email not sent): %s", to_email, otp)


def create_password_reset_flow(db: Session, user: User) -> str:
    """Invalidate old tokens, create new OTP, return plain OTP for email."""
    db.query(PasswordResetToken).filter(
        PasswordResetToken.user_id == user.id, PasswordResetToken.used.is_(False)
    ).update({"used": True})
    otp = generate_otp()
    row = PasswordResetToken(
        user_id=user.id,
        otp_hash=hash_password(otp),
        expires_at=datetime.now(timezone.utc) + timedelta(minutes=15),
        used=False,
    )
    db.add(row)
    db.commit()
    return otp
