from models.all_sequences_model import AllSequencesModel
from repository.file_system_repository import FileSystemRepository
from typing import List, Dict, Any
import json

from models.var import from_str, from_list, from_dict


def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value


def to_ascii_hash(text):
    ascii_values = [ord(character) for character in text]
    return reduce(lambda x, y: x + y, ascii_values)


class AdjacencyMatrixModel:
    matrixError: Dict[str, str] = {}
    all_parts: List[str]
    first_detail: str
    matrix: Dict[str, List[str]]

    fileName = "adjacency_matrix.json"

    def __init__(
        self, all_parts: List[str], first_detail: str, matrix: Dict[str, List[str]]
    ) -> None:
        self.all_parts = all_parts
        self.first_detail = first_detail
        self.matrix = matrix
        self.validateMatrix()

    def matrixToFileSystem(self, path: str):
        FileSystemRepository.writeFile(
            json.dumps(self.to_dict(), ensure_ascii=False, indent=4),
            path,
            AdjacencyMatrixModel.fileName,
        )
        pass

    def sequencesToFileSystem(self, path: str, restrictions: list[str]):
        FileSystemRepository.writeFile(
            json.dumps(
                {
                    "sequences": AllSequencesModel(
                        self.matrix, restrictions
                    ).adj_matrix_names
                },
                ensure_ascii=False,
                indent=4,
            ),
            path,
            "sequences.json",
        ),
        pass

    def matrixGetUniqueContact(self):
        detailsToCheck = []
        detailsHashCheck = {}
        for k, v in self.matrix.items():
            for el in v:
                if el != k:
                    hash = to_ascii_hash(k + el)
                    if detailsHashCheck.get(hash) == None:
                        detailsHashCheck[hash] = hash
                        detailsToCheck.append({"child": el, "parent": k})
        return detailsToCheck

    def whatPlaceLeadingPartIndex(self):
        i = 0
        for el in self.matrix:
            if el == self.first_detail:
                return i
            i = +1

    def validateMatrix(self):
        for el in self.all_parts:
            if self.matrix.get(el) == None:
                self.matrixError[el] = "Not found adjacency " + el

    @staticmethod
    def from_dict(obj: Any) -> "AdjacencyMatrixModel":
        assert isinstance(obj, dict)

        all_pars = from_list(from_str, obj.get("allParts"))
        first_detail = from_str(obj.get("firstDetail"))
        matrix = from_dict(lambda x: from_list(from_str, x), obj.get("matrix"))

        return AdjacencyMatrixModel(all_pars, first_detail, matrix)

    def to_dict(self) -> dict:
        result: dict = {}
        result["allParts"] = from_list(from_str, self.all_parts)
        result["firstDetail"] = from_str(self.first_detail)
        result["matrix"] = from_dict(lambda x: from_list(from_str, x), self.matrix)
        if self.matrixError.values().__len__() == 0:
            result["matrixError"] = None
        else:
            result["matrixError"] = self.matrixError
        return result

    def getDictMatrix(self) -> dict:
        result = {}

        for k, v in self.matrix.items():
            result[k] = {}
            for el in v:
                result[k][el] = el

        return result
