# services/auth/app/repositories/password_reset_repository.py
"""
Password reset token repository
Repository for password reset token operations
"""

from typing import Optional, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, text
from datetime import datetime, timezone
import structlog
import uuid

from .base import AuthBaseRepository
from app.models.password_reset_tokens import PasswordResetToken
from shared.database.exceptions import DatabaseError

logger = structlog.get_logger()


class PasswordResetTokenRepository(AuthBaseRepository):
    """Repository for password reset token operations"""

    def __init__(self, session: AsyncSession):
        super().__init__(PasswordResetToken, session)

    async def create_token(self, user_id: str, token: str, expires_at: datetime) -> PasswordResetToken:
        """Create a new password reset token"""
        try:
            token_data = {
                "user_id": user_id,
                "token": token,
                "expires_at": expires_at,
                "is_used": False
            }

            reset_token = await self.create(token_data)

            logger.debug("Password reset token created",
                        user_id=user_id,
                        token_id=reset_token.id,
                        expires_at=expires_at)

            return reset_token

        except Exception as e:
            logger.error("Failed to create password reset token",
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to create password reset token: {str(e)}")

    async def get_token_by_value(self, token: str) -> Optional[PasswordResetToken]:
        """Get password reset token by token value"""
        try:
            stmt = select(PasswordResetToken).where(
                and_(
                    PasswordResetToken.token == token,
                    PasswordResetToken.is_used == False,
                    PasswordResetToken.expires_at > datetime.now(timezone.utc)
                )
            )
            
            result = await self.session.execute(stmt)
            return result.scalar_one_or_none()

        except Exception as e:
            logger.error("Failed to get password reset token by value", error=str(e))
            raise DatabaseError(f"Failed to get password reset token: {str(e)}")

    async def mark_token_as_used(self, token_id: str) -> Optional[PasswordResetToken]:
        """Mark a password reset token as used"""
        try:
            return await self.update(token_id, {
                "is_used": True,
                "used_at": datetime.now(timezone.utc)
            })
        except Exception as e:
            logger.error("Failed to mark password reset token as used",
                        token_id=token_id,
                        error=str(e))
            raise DatabaseError(f"Failed to mark token as used: {str(e)}")

    async def cleanup_expired_tokens(self) -> int:
        """Clean up expired password reset tokens"""
        try:
            now = datetime.now(timezone.utc)

            # Delete expired tokens
            query = text("""
                DELETE FROM password_reset_tokens
                WHERE expires_at < :now OR is_used = true
            """)

            result = await self.session.execute(query, {"now": now})
            deleted_count = result.rowcount

            logger.info("Cleaned up expired password reset tokens",
                       deleted_count=deleted_count)

            return deleted_count

        except Exception as e:
            logger.error("Failed to cleanup expired password reset tokens", error=str(e))
            raise DatabaseError(f"Token cleanup failed: {str(e)}")

    async def get_valid_token_for_user(self, user_id: str) -> Optional[PasswordResetToken]:
        """Get a valid (unused, not expired) password reset token for a user"""
        try:
            stmt = select(PasswordResetToken).where(
                and_(
                    PasswordResetToken.user_id == user_id,
                    PasswordResetToken.is_used == False,
                    PasswordResetToken.expires_at > datetime.now(timezone.utc)
                )
            ).order_by(PasswordResetToken.created_at.desc())
            
            result = await self.session.execute(stmt)
            return result.scalar_one_or_none()

        except Exception as e:
            logger.error("Failed to get valid token for user",
                        user_id=user_id,
                        error=str(e))
            raise DatabaseError(f"Failed to get valid token for user: {str(e)}")