# app/modules/tenancy/models/email_verification_token.py
"""
Email verification token model for user accounts.

Security features:
- Tokens are stored as SHA256 hashes, not plaintext
- Tokens expire after 24 hours
- Only one active token per user (old tokens invalidated on new request)
"""

import hashlib
import secrets
from datetime import datetime, timedelta

from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import Session, relationship

from app.core.database import Base


class EmailVerificationToken(Base):
    """Email verification token for user accounts."""

    __tablename__ = "email_verification_tokens"

    # Token expiry in hours
    TOKEN_EXPIRY_HOURS = 24

    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(
        Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False
    )
    token_hash = Column(String(64), nullable=False, index=True)
    expires_at = Column(DateTime, nullable=False)
    used_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)

    # Relationships
    user = relationship("User")

    def __repr__(self):
        return f"<EmailVerificationToken(id={self.id}, user_id={self.user_id}, expires_at={self.expires_at})>"

    @staticmethod
    def hash_token(token: str) -> str:
        """Hash a token using SHA256."""
        return hashlib.sha256(token.encode()).hexdigest()

    @classmethod
    def create_for_user(cls, db: Session, user_id: int) -> str:
        """Create a new email verification token for a user.

        Invalidates any existing tokens for the user.
        Returns the plaintext token (to be sent via email).
        """
        # Invalidate existing tokens for this user
        db.query(cls).filter(
            cls.user_id == user_id,
            cls.used_at.is_(None),
        ).delete()

        # Generate new token
        plaintext_token = secrets.token_urlsafe(32)
        token_hash = cls.hash_token(plaintext_token)

        # Create token record
        token = cls(
            user_id=user_id,
            token_hash=token_hash,
            expires_at=datetime.utcnow() + timedelta(hours=cls.TOKEN_EXPIRY_HOURS),
        )
        db.add(token)
        db.flush()

        return plaintext_token

    @classmethod
    def find_valid_token(cls, db: Session, plaintext_token: str) -> "EmailVerificationToken | None":
        """Find a valid (not expired, not used) token."""
        token_hash = cls.hash_token(plaintext_token)

        return db.query(cls).filter(
            cls.token_hash == token_hash,
            cls.expires_at > datetime.utcnow(),
            cls.used_at.is_(None),
        ).first()

    def mark_used(self, db: Session) -> None:
        """Mark this token as used."""
        self.used_at = datetime.utcnow()
        db.flush()
