import re
from ..datasets.dataset_commons import (
    get_dataset_schema,
    get_dataset_settings_and_dictionary,
    get_dataset_in_connection_settings,
)
from ..folders.folder_commons import get_managed_folder_id
from ...type_checking import DSSProject, check_object_is_project


def change_filesystem_dataset_path(project: DSSProject, dataset_name: str, path: str):
    """
    Changes the path of a filesystem project dataset

    :param project: DSSProject: A handle to interact with a project on the DSS instance.
    :param dataset_name: str: Name of the dataset.
    :param path: str: New dataset path.
    """
    check_object_is_project(project)
    dataset_settings, __ = get_dataset_settings_and_dictionary(project, dataset_name, False)
    dataset_settings.settings["params"]["path"] = path
    dataset_settings.save()
    pass


def change_folder_path(project: DSSProject, folder_name: str, path: str):
    """
    Changes the path of a project folder.

    :param project: DSSProject: A handle to interact with a project on the DSS instance.
    :param folder_name: str: Name of the folder.
    :param path: str: New folder path.
    """
    check_object_is_project(project)
    folder_id = get_managed_folder_id(project, folder_name)
    folder = project.get_managed_folder(folder_id)
    folder_definition = folder.get_definition()
    folder_definition["params"]["path"] = path
    folder.set_definition(folder_definition)
    pass


def change_filesystem_dataset_format(project: DSSProject, dataset_name: str, new_dataset_format: str):
    """
    Changes the files format of a filesystem project dataset.

    :param project: DSSProject: A handle to interact with a project on the DSS instance.
    :param dataset_name: str: Name of the dataset.
    :param new_dataset_format: str: New dataset files format.
    """
    ALLOWED_FORMATS = ["csv", "parquet", "avro"]
    check_object_is_project(project)
    if new_dataset_format not in ALLOWED_FORMATS:
        log_message = "File format '{}' is not supported by this function, please " "choose a format in '{}'".format(
            new_dataset_format, ALLOWED_FORMATS
        )
        raise Exception(log_message)
    dataset_settings, __ = get_dataset_settings_and_dictionary(project, dataset_name, False)
    previous_format = dataset_settings.settings["formatType"]
    print(
        "Switching dataset '{}' format from '{}' to '{}' ...".format(dataset_name, previous_format, new_dataset_format)
    )
    dataset_settings.settings["formatType"] = new_dataset_format
    dataset_settings.save()
    print("Dataset format '{}' switched".format(dataset_name))
    pass


def switch_not_managed_dataset_connection_to_cloud_storage(
    project: DSSProject, dataset_name: str, connection_name: str, dataset_path_in_connection: str
):
    """
    Changes the connection of a NOT managed DSS dataset toward a clould storage connection.
    Connection must have a type in ['Azure', 'GCS', 'S3'].

    :param project: DSSProject: A handle to interact with a project on the DSS instance.
    :param dataset_name: str: Name of the dataset.
    :param connection_name: str: Name of the cloud storage connection.
    :param dataset_path_in_connection: str: Dataset path in the cloud storage connection.
    """
    ALLOWED_CLOUD_STORAGES = ["Azure", "GCS", "S3"]
    check_object_is_project(project)
    dataset_settings, __ = get_dataset_settings_and_dictionary(project, dataset_name, False)
    dataset_is_managed = dataset_settings.settings["managed"]

    if dataset_is_managed:
        log_message = (
            "Dataset '{}' is a DSS managed dataset.\n"
            "You can't use this function to change its connection".format(dataset_name)
        )
        raise Exception(log_message)

    dataset_connection_settings = get_dataset_in_connection_settings(project, connection_name)
    dataset_connection_settings["managed"] = False
    connection_type = dataset_connection_settings["type"]

    if connection_type not in ALLOWED_CLOUD_STORAGES:
        log_message = (
            "Connection '{}' is of type '{}' that is not allowed by this function.\n"
            "Allowed connection types are '{}'".format(connection_name, connection_type, ALLOWED_CLOUD_STORAGES)
        )
        raise Exception(log_message)

    dataset_connection_settings["params"]["path"] = dataset_path_in_connection
    dataset_connection_settings["name"] = dataset_name
    metastore_synchronization_enabled = dataset_connection_settings["params"]["metastoreSynchronizationEnabled"]
    if metastore_synchronization_enabled:
        dataset_connection_settings["params"]["metastoreTableName"] = dataset_name
    else:
        dataset_connection_settings["params"]["metastoreTableName"] = ""

    dataset_settings.settings = dataset_connection_settings
    dataset_settings.save()
    pass


def switch_managed_dataset_connection_to_cloud_storage(project: DSSProject, dataset_name: str, connection_name: str):
    """
    Changes the connection of a managed DSS dataset toward a clould storage connection.
    Connection must have a type in ['Azure', 'GCS', 'S3'].

    :param project: DSSProject: A handle to interact with a project on the DSS instance.
    :param dataset_name: str: Name of the dataset.
    :param connection_name: str: Name of the cloud storage connection.
    """
    ALLOWED_CLOUD_STORAGES = ["Azure", "GCS", "S3"]
    check_object_is_project(project)
    dataset_settings, __ = get_dataset_settings_and_dictionary(project, dataset_name, False)
    dataset_is_managed = dataset_settings.settings["managed"]

    if not dataset_is_managed:
        log_message = (
            "Dataset '{}' is not a DSS managed dataset.\n"
            "You can't use this function to change its connection".format(dataset_name)
        )
        raise Exception(log_message)

    dataset_connection_settings = get_dataset_in_connection_settings(project, connection_name)
    connection_type = dataset_connection_settings["type"]

    if connection_type not in ALLOWED_CLOUD_STORAGES:
        log_message = (
            "Connection '{}' is of type '{}' that is not allowed by this function.\n"
            "Allowed connection types are '{}'".format(connection_name, connection_type, ALLOWED_CLOUD_STORAGES)
        )
        raise Exception(log_message)

    connection_path = dataset_connection_settings["params"]["path"]
    connection_path = re.sub("dataset_for_connection_settings_extraction", "", connection_path)
    new_connection_path = "{}{}".format(connection_path, dataset_name)
    dataset_connection_settings["params"]["path"] = new_connection_path

    dataset_connection_settings["name"] = dataset_name
    metastore_synchronization_enabled = dataset_connection_settings["params"]["metastoreSynchronizationEnabled"]
    if metastore_synchronization_enabled:
        dataset_connection_settings["params"]["metastoreTableName"] = dataset_name
    else:
        dataset_connection_settings["params"]["metastoreTableName"] = ""

    dataset_connection_settings["schema"]["columns"] = get_dataset_schema(project, dataset_name)
    dataset_connection_settings["metrics"] = dataset_settings.settings["metrics"]
    dataset_settings.settings = dataset_connection_settings
    dataset_settings.save()
    pass


def switch_managed_dataset_connection_to_local_filesytem_storage(project: DSSProject, dataset_name: str, local_filesystem_connection_name: str):
    """
    Changes the connection of a managed DSS dataset toward a a local filesystem storage.
    Connection must have a type equals to "Filesystem".

    :param project: DSSProject: A handle to interact with a project on the DSS instance.
    :param dataset_name: str: Name of the dataset.
    :param local_filesystem_connection_name: str: Name of the local filesystem connection.
    """
    check_object_is_project(project)
    dataset_settings, __ = get_dataset_settings_and_dictionary(project, dataset_name, False)
    dataset_is_managed = dataset_settings.settings["managed"]

    if not dataset_is_managed:
        log_message = (
            "Dataset '{}' is not a DSS managed dataset.\n"
            "You can't use this function to change its connection".format(dataset_name)
        )
        raise Exception(log_message)

    dataset_connection_settings = get_dataset_in_connection_settings(project, local_filesystem_connection_name)
    connection_type = dataset_connection_settings["type"]

    if connection_type != "Filesystem":
        log_message = ("Connection '{}' is of type '{}' that is not allowed by this function.\n"
        "Only allowed connection types is 'Filesystem'".format(local_filesystem_connection_name, connection_type)
        )
        raise Exception(log_message)

    connection_path = dataset_connection_settings["params"]["path"]
    connection_path = re.sub("dataset_for_connection_settings_extraction", "", connection_path)
    new_connection_path = "{}{}".format(connection_path, dataset_name)
    dataset_connection_settings["params"]["path"] = new_connection_path
    dataset_connection_settings["name"] = dataset_name
    dataset_connection_settings["schema"]["columns"] = get_dataset_schema(project, dataset_name)
    dataset_connection_settings["metrics"] = dataset_settings.settings["metrics"]
    dataset_settings.settings = dataset_connection_settings
    dataset_settings.save()
    pass


def switch_managed_folder_connection(project: DSSProject, folder_name: str, connection_name: str):
    """
    Changes the connection of a managed DSS folder toward a clould storage connection.
    Connection must have a type in ['S3', 'Azure', 'GCS', 'Filesystem'].

    :param project: DSSProject: A handle to interact with a project on the DSS instance.
    :param folder_name: str: Name of the folder.
    :param connection_name: str: Name of the cloud storage connection.
    """
    check_object_is_project(project)
    ALLOWED_CLOUD_STORAGES = ["S3", "Azure", "GCS"]
    ALLOWED_STORAGES = ALLOWED_CLOUD_STORAGES + ["Filesystem"]
    
    folder_id = get_managed_folder_id(project, folder_name)
    folder = project.get_managed_folder(folder_id)
    folder_definition = folder.get_definition()

    dataset_connection_settings = get_dataset_in_connection_settings(project, connection_name)
    connection_type = dataset_connection_settings["type"]

    if connection_type not in ALLOWED_STORAGES:
        log_message = (
            "Connection '{}' is of type '{}' that is not allowed by this function.\n"
            "Allowed connection types are '{}'".format(connection_name, connection_type, ALLOWED_STORAGES)
        )
        raise Exception(log_message)

    folder_definition["type"] = connection_type
    connection_path = dataset_connection_settings["params"]["path"]
    connection_path = re.sub("dataset_for_connection_settings_extraction", "", connection_path)
    new_connection_path = "{}{}".format(connection_path, "${odbId}")
    folder_definition["params"]["path"] = new_connection_path
    folder_definition["params"]["connection"] = connection_name
    
    if connection_type in ALLOWED_CLOUD_STORAGES:
        metastore_synchronization_enabled = dataset_connection_settings["params"]["metastoreSynchronizationEnabled"]
        if metastore_synchronization_enabled:
            folder_definition["params"]["metastoreTableName"] = "${odbId}"
        else:
            folder_definition["params"]["metastoreTableName"] = ""

    folder.set_definition(folder_definition)
    pass