# Copyright (c) 2026 Emanuele Bellocchia
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

"""Module for implementing algorithms for brainwallet generation."""

# Imports
from enum import Enum, auto, unique
from typing import Any

from typing_extensions import override

from bip_utils.brainwallet.ibrainwallet_algo import IBrainwalletAlgo
from bip_utils.utils.crypto import DoubleSha256, Pbkdf2HmacSha512, Scrypt, Sha256


@unique
class BrainwalletAlgos(Enum):
    """Enum for brainwallet algorithms."""

    SHA256 = auto()
    DOUBLE_SHA256 = auto()
    PBKDF2_HMAC_SHA512 = auto()
    SCRYPT = auto()


class BrainwalletAlgoConst:
    """Class container for brainwallet algorithm constants."""

    PBKDF2_HMAC_SHA512_KEY_LEN: int = 32
    PBKDF2_HMAC_SHA512_DEF_ITR_NUM: int = 2 * 1024 * 1024
    SCRYPT_KEY_LEN: int = 32
    SCRYPT_DEF_N: int = 128 * 1024
    SCRYPT_DEF_P: int = 8
    SCRYPT_DEF_R: int = 8


class BrainwalletAlgoSha256(IBrainwalletAlgo):
    """Compute the private key from passphrase using SHA256 algorithm."""

    @override
    @staticmethod
    def ComputePrivateKey(passphrase: str,
                          **kwargs: Any) -> bytes:
        """
        Compute the private key from the specified passphrase.

        Args:
            passphrase (str): Passphrase
            **kwargs        : Not used

        Returns:
            bytes: Private key bytes
        """
        return Sha256.QuickDigest(passphrase)


class BrainwalletAlgoDoubleSha256(IBrainwalletAlgo):
    """Compute the private key from passphrase using double SHA256 algorithm."""

    @override
    @staticmethod
    def ComputePrivateKey(passphrase: str,
                          **kwargs: Any) -> bytes:
        """
        Compute the private key from the specified passphrase.

        Args:
            passphrase (str): Passphrase
            **kwargs        : Not used

        Returns:
            bytes: Private key bytes
        """
        return DoubleSha256.QuickDigest(passphrase)


class BrainwalletAlgoPbkdf2HmacSha512(IBrainwalletAlgo):
    """Compute the private key from passphrase using PBKDF2 HMAC-SHA512 algorithm."""

    @override
    @staticmethod
    def ComputePrivateKey(passphrase: str,
                          **kwargs: Any) -> bytes:
        """
        Compute the private key from the specified passphrase.

        Args:
            passphrase (str): Passphrase

        Other Parameters:
            salt (str)   : Salt for PBKDF2 algorithm (default: empty)
            itr_num (int): Number of iteration for PBKDF2 algorithm (default: 2097152)

        Returns:
            bytes: Private key bytes
        """
        salt = kwargs.get("salt", "")
        itr_num = kwargs.get("itr_num", BrainwalletAlgoConst.PBKDF2_HMAC_SHA512_DEF_ITR_NUM)

        return Pbkdf2HmacSha512.DeriveKey(
            passphrase,
            salt,
            itr_num=itr_num,
            dklen=BrainwalletAlgoConst.PBKDF2_HMAC_SHA512_KEY_LEN
        )


class BrainwalletAlgoScrypt(IBrainwalletAlgo):
    """Compute the private key from passphrase using Scrypt algorithm."""

    @override
    @staticmethod
    def ComputePrivateKey(passphrase: str,
                          **kwargs: Any) -> bytes:
        """
        Compute the private key from the specified passphrase.

        Args:
            passphrase (str): Passphrase

        Other Parameters:
            salt (str): Salt for Scrypt algorithm (default: empty)
            n (int)   : CPU/Memory cost parameter for Scrypt algorithm (default: 131072)
            r (int)   : Block size parameter for Scrypt algorithm (default: 8)
            p (int)   : Parallelization parameter for Scrypt algorithm (default: 8)

        Returns:
            bytes: Private key bytes
        """
        salt = kwargs.get("salt", "")
        n = kwargs.get("n", BrainwalletAlgoConst.SCRYPT_DEF_N)
        r = kwargs.get("r", BrainwalletAlgoConst.SCRYPT_DEF_R)
        p = kwargs.get("p", BrainwalletAlgoConst.SCRYPT_DEF_P)

        return Scrypt.DeriveKey(
            passphrase,
            salt,
            key_len=BrainwalletAlgoConst.SCRYPT_KEY_LEN,
            n=n,
            r=r,
            p=p
        )