from typing import List, Tuple, Union

from openlog.datamodel.assay.assay_factory import AssayFactory
from openlog.datamodel.assay.generic_assay import AssayDomainType
from openlog.datamodel.connection.openlog_connection import OpenLogConnection
from openlog.gui.assay_visualization.config.assay_visualization_config import (
    AssayVisualizationConfig,
    AssayVisualizationConfigList,
)
from openlog.gui.assay_visualization.config_factory import (
    AssayColumnVisualizationConfigFactory,
)
from openlog.toolbelt.log_handler import PlgLogger

log = PlgLogger().log


class ConfigurationReadException(Exception):
    pass


def from_json(
    data: dict,
    connection: OpenLogConnection,
    domain: Union[AssayDomainType, None] = None,
) -> Tuple[AssayVisualizationConfigList, List[str]]:
    """
    Define AssayVisualizationConfigList from json data

    Args:
        data: json data

    Returns: AssayVisualizationConfigList, List[str] errors list

    """
    errors = []
    res = AssayVisualizationConfigList()
    for conf_data in data["configs"]:
        try:
            config = config_from_json(conf_data, connection, domain)
            if config:
                res.list.append(config)
        except Exception as exc:
            errors.append(str(exc))
            log(message="Invalid configuration for current connection : {}".format(exc))
    return res, errors


def config_from_json(
    data: dict,
    connection: OpenLogConnection,
    domain: Union[AssayDomainType, None] = None,
) -> Union[AssayVisualizationConfig, None]:
    """
    Define AssayVisualizationConfig from json data (see to_dict)

    Args:
        data: json data

    Returns: Union[AssayVisualizationConfig, None]

    """
    res = AssayVisualizationConfig(hole_id=data["hole_id"], assay=data["assay_name"])
    res.from_json(data)

    assay = AssayFactory().create_assay(connection, res.hole_id, res.assay_name)

    # Check if domain is compatible
    if domain and assay.assay_definition.domain != domain:
        return None

    for col, config_data in data["configs"].items():
        # Check if colum available in assay definition
        if col not in assay.assay_definition.columns:
            raise ConfigurationReadException(
                "Column '{}' not available in assay '{}'".format(
                    col, assay.assay_definition.variable
                )
            )

        config = AssayColumnVisualizationConfigFactory().create_config(
            assay.assay_definition, col, connection.get_assay_iface()
        )
        config.assay = assay
        config.assay_display_name = assay.assay_definition.display_name
        config.assay_name = assay.assay_definition.variable
        config.from_json(config_data)
        res.add_column_config(config)

    return res
