"""
Copyright 2024 BlackRock, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import logging
from pathlib import Path
import yaml
from cryptography.fernet import Fernet
import os.path
import base64

_logger = logging.getLogger(__name__)

"""
Provides basic utilities to read and parse
secrets from a file accessible on file system. Intended to be used for user's
local development environment or in Aladdin Compute sessions to read secret files
injected by Vault init container.

Note: Read from yaml currently supports reading values at first level
"""

_default_key_for_enc = b'T10Zcv_jbwBI2KANbEoytpeK6zVbP_F5owJajX-sSGA='


def read_base64_enc_file(path):
    """
    Given a file path to a secret file containing base64 encrypted secret, read and decrypt the contents and return as string

    Args:
        path (filepath): path to a secret file containing base64 encrypted password
    Returns:
        decrypted contents of the file
    """
    enc_secret = read_secret_from_file(path)
    dec_secret = base64.b64decode(enc_secret)

    if isinstance(dec_secret, bytes):
        dec_secret = dec_secret.decode("utf-8")

    return dec_secret


def read_secret_from_file(path, default_val=None):
    """
    Given a file path to a secret file, read the contents and return as string

    Args:
        path (filepath): path to a secret file
    Returns:
        contents of the file
    """
    try:
        if os.path.exists(path) and os.access(path, os.R_OK) and os.path.isfile(path):
            contents = Path(path).read_text()
            return contents
        else:
            raise FileNotFoundError('Unable to validate file path and permissions')
    except FileNotFoundError as e:
        _logger.debug(e)
        return default_val


def read_secret_from_yaml_file(path, key, default_val=None):
    """
    Given a path to a yaml file, and a key, return the value

    Args:
        path(string): File path to yaml file containing secrets
        key(string): secret key
    """
    try:
        secret_file_content = _read_yaml_file(path)
        return secret_file_content[key]
    except (FileNotFoundError, yaml.YAMLError, KeyError, NotADirectoryError) as e:
        _logger.debug(e)
        return default_val


def _read_yaml_file(path):
    """
    Read and return contents of given yaml file

    Args:
        path (string): File path to yaml file containing secrets
    """
    with open(path, "r") as stream:
        try:
            secret_data = yaml.safe_load(stream)
            return secret_data
        except yaml.YAMLError as e:
            _logger.debug(e)
            raise e


def store_encrypted_content_in_file(plain_text_secret, filepath_to_encrypted_secret, encryption_key=_default_key_for_enc,
                                    filepath_to_encryption_key=None):
    """
    This method takes plain text secret (e.g. password), path to where encrypted secret needs to be stored, optionally an encryption key or
    path to a file storing encryption key
    This will be used one time by the user outside of the general execution

    Args:
        plain_text_secret (string): Your plain text secret.
        filepath_to_encrypted_secret (string): The full path of the encrypted secret file.
        encryption_key (bytes): The Fernet key must be 32 url-safe base64-encoded bytes, generated by Fernet.generate_key()
        path_to_encryption_key (string): The full path of your encryption key.
    """
    if filepath_to_encryption_key and os.path.isfile(filepath_to_encryption_key):
        encryption_key = open(filepath_to_encryption_key, 'rb').read()

    cipher_suite = Fernet(encryption_key)
    encrypted_password = cipher_suite.encrypt(bytes(plain_text_secret, 'utf-8'))
    with open(filepath_to_encrypted_secret, "wb") as encrypted_password_file:
        encrypted_password_file.write(encrypted_password)


def decrypt_file_content(filepath_to_encrypted_secret, encryption_key=_default_key_for_enc, filepath_to_encryption_key=None):
    """
    Decrypts password and passes back to caller

    Args:
        filepath_to_encrypted_secret (string): The full path of the encrypted password file.
        encryption_key (bytes): The Fernet key must be 32 url-safe base64-encoded bytes, generated by Fernet.generate_key()
        path_to_encryption_key (string): The full path of your encryption key.
    """
    if filepath_to_encryption_key and os.path.isfile(filepath_to_encryption_key):
        encryption_key = open(filepath_to_encryption_key, 'rb').read()

    password = None
    cipher_suite = Fernet(encryption_key)
    if filepath_to_encrypted_secret and os.path.isfile(filepath_to_encrypted_secret):
        password = cipher_suite.decrypt(open(filepath_to_encrypted_secret, 'rb').read().strip()).decode("utf-8")
    return password
