# Copyright (C) 2011-2024 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/

from rhodecode.lib.str_utils import safe_bytes
from rhodecode.lib.encrypt import encrypt_data, validate_and_decrypt_data
from rhodecode.lib.encrypt2 import Encryptor

ALLOWED_ALGOS = ["aes", "fernet"]


def get_default_algo():
    import rhodecode

    return rhodecode.CONFIG.get("rhodecode.encrypted_values.algorithm") or "aes"


def get_strict_mode():
    import rhodecode

    return rhodecode.ConfigGet().get_bool("rhodecode.encrypted_values.strict") or False


def encrypt_value(value: bytes, enc_key: bytes, algo: str = ""):
    if not algo:
        # not explicit algo, just use what's set by config
        algo = get_default_algo()

    if algo not in ALLOWED_ALGOS:
        ValueError(f"Bad encryption algorithm, should be {ALLOWED_ALGOS}, got: {algo}")

    enc_key = safe_bytes(enc_key)
    value = safe_bytes(value)

    if algo == "aes":
        return encrypt_data(value, enc_key=enc_key)
    if algo == "fernet":
        return Encryptor(enc_key).encrypt(value)

    return value


def decrypt_value(value: bytes, enc_key: bytes, algo: str = "", strict_mode: bool | None = None):
    if strict_mode is None:
        # we use config value rather then explicit True/False
        strict_mode = get_strict_mode()

    enc_key = safe_bytes(enc_key)
    value = safe_bytes(value)

    if not algo:
        # not explicit algo, just use what's set by config
        algo = Encryptor.detect_enc_algo(value) or get_default_algo()
    if algo not in ALLOWED_ALGOS:
        ValueError(f"Bad encryption algorithm, should be {ALLOWED_ALGOS}, got: {algo}")

    safe = not strict_mode

    if algo == "aes":
        return validate_and_decrypt_data(value, enc_key, safe=safe)
    if algo == "fernet":
        return Encryptor(enc_key).decrypt(value, safe=safe)

    return value
