import os
import json
from typing import List, cast


class DotDict(dict):
    """A dictionary that supports dot notation and dictionary-style access."""

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(f"No such attribute: {name}")

    def __setattr__(self, name, value):
        self[name] = value

    def __delattr__(self, name):
        try:
            del self[name]
        except KeyError:
            raise AttributeError(f"No such attribute: {name}")


def load_args_from_json(folder_path):
    # Find JSON files starting with 'args_'
    json_files = [f for f in os.listdir(folder_path) if f.startswith('args_') and f.endswith('.json')]

    # Check if there is exactly one such file
    if len(json_files) == 1:
        json_file_path = os.path.join(folder_path, json_files[0])
        print(f"Loading arguments from: {json_file_path}")

        # Load the JSON file into a dictionary
        with open(json_file_path, 'r') as json_file:
            args_dict = json.load(json_file)

        return DotDict(args_dict)
    elif len(json_files) > 1:
        raise Exception(f"More than one 'args_' JSON file found in {folder_path}.")
    else:
        raise Exception(f"No 'args_' JSON file found in {folder_path}.")


def identifier(**KWARGS) -> str:
    R"""
    Get identifier of given arguments.
    """
    #
    return (
        "{:s}~{:s}~{:s}_{:s}~{:s}~{:s}_{:s}~{:s}~{:s}~{:s}_{:s}".format(
            "{:s}~{:s}".format(
                cast(str, KWARGS["source"]), cast(str, KWARGS["train_prop"]),
            )
            if "train_prop" in KWARGS else
            cast(str, KWARGS["source"]),
            cast(str, KWARGS["target"]),
            {"transductive": "trans", "inductive": "induc"}
            [cast(str, KWARGS["framework"])],
            "{:s}-preseq".format(cast(str, KWARGS["model"]))
            if (
                (
                    "pretrain_seq_node" in KWARGS
                    and len(KWARGS["pretrain_seq_node"]) > 0
                )
                or (
                    "pretrain_seq_edge" in KWARGS
                    and len(KWARGS["pretrain_seq_edge"]) > 0
                )
            ) else
            cast(str, KWARGS["model"]),
            cast(str, KWARGS["hidden"]),
            cast(str, KWARGS["activate"]), cast(str, KWARGS["lr"]),
            cast(str, KWARGS["weight_decay"]), cast(str, KWARGS["clipper"]),
            cast(str, KWARGS["patience"]), cast(str, KWARGS["seed"]),
        )
    )
