"""

    AUTHOR:

        Khushal P Soonderji

    DATE:

        Tuesday, 10th Sept., 2024

    OBJECTIVE:

        To provide a way to generate and verify OTPs.

    REFERENCES:

        01. https://pyauth.github.io/pyotp/#
        02. https://en.wikipedia.org/wiki/Google_Authenticator

    DOWNLOADS:

        N/A

"""


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                   IMPORT                                                      ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")

# To work with OTPs:
import pyotp
import base64
import hashlib

# To work with date and time:
import time
import datetime

# For debugging:
from icecream import IceCreamDebugger


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                           MACROS / ONE-TIME INIT                                              ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                VARIABLES                                                      ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                   CLASSES                                                     ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


class HashedOTP:

    def __init__(
            self,
            secret
    ):

        """
        Used to generate and verify HMAC-based OTPs.
        :param secret: The key to use to generate and verify OTPs.
        """

        # Note down the input variables:
        self.__secret = secret
        self.__otp_client = pyotp.HOTP(secret)

    @staticmethod
    def generate_secret(message = None):

        """
        Generate a secret key to then use to generate and verify the OTPs.
        You may override the random generator by giving a "message" of any length.
        :param message: A custom value to convert into a key. Avoid using this for better security, but this can be used
            to generate keys based on user identifiers. THERE IS NO RANDOMNESS IF YOU USE THIS FEATURE. IT IS FOR
            CONVENIENCE ONLY. NOT RECOMMENDED.
        :return: The key (as a string) that can be used to generate and verify the OTPs.
        """

        # If the user wants to generate a key from a custom input:
        if message:

            # Ensure we have a bytes object:
            if not isinstance(message, (str, bytes)): message = str(message)
            if isinstance(message, str): message = message.encode("utf-8")

            # Hash the bytes object:
            sha256_hash = hashlib.sha256()
            sha256_hash.update(message)
            hashed_key = sha256_hash.digest()

            # Convert to base-32:
            return base64.b32encode(hashed_key).decode("utf-8")

        # If the user wants a totally random key:
        else: return pyotp.random_base32()

    def generate_otp(self, count: int):

        """
        Generates the OTP at a particular step.
        :param count: The step at which the OTP needs to be generated.
        :return: The OTP string (6 digits).
        """

        return str(self.__otp_client.at(count))

    def verify_otp(self, otp, count: int):

        """
        Verifies the claimed OTP.
        :param otp: The OTP as claimed by the end user.
        :param count: The step at which the OTP needs to be verified.
        :return: True if the OTP is valid, else False.
        """

        return self.__otp_client.verify(otp, counter = count)


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                               MAIN PROGRAM                                                    ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


if __name__ == "__main__":

    pass
