from extensions.list import CoreList
from itertools import repeat


class AllSequencesModel:
    """
    A class that processes information from the assembly and creates the objects necessary to create the assembly sequence

    """

    all_sequences = None
    adj_matrix = None
    topologyIds = None
    adj_matrix_names = None

    def __init__(self, adj_matrix, restrictions: list[str]) -> None:
        self.adj_matrix = adj_matrix
        self.all_possible_sequences(self.adj_matrix)
        self.matrix_by_name()
        if restrictions.__len__() != 0:
            self.restrictionsValidate(restrictions)
        pass

    def restrictionsValidate(self, restrictions: CoreList[str]):
        """_summary_

        Args:
            restrictions (CoreList[str]): _description_
        """
        filterMatrix = CoreList()

        for el in self.adj_matrix_names:
            result = False
            for restraint in restrictions:
                result = CoreList(el).indexedPriorities(restraint[0], restraint[1])
                if result:
                    filterMatrix.onlyUniqueElementAppend(el)
        self.adj_matrix_names = filterMatrix
        pass

    def matrix_by_name(self):
        result = self.all_sequences
        inc = 0
        for matrix in self.all_sequences:
            for index in range(len(matrix)):
                result[inc][index] = CoreList(
                    filter(
                        lambda el: el.get("number") == matrix[index] + 1,
                        self.topologyIds,
                    )
                )[0].get("name")
            inc += 1
        self.adj_matrix_names = result
        pass

    def find_all_sequences(self, adj_matrix):
        """Find all assembly sequences with dfs

        Args:
            adj_matrix (dict): Assembly ajacency matrix
        """
        sequences = []
        num_vertices = len(adj_matrix)

        def dfs(vertex, sequence):
            sequence.append(vertex)

            if len(sequence) == num_vertices:
                sequences.append(sequence)
                return

            for i in range(num_vertices):
                if adj_matrix[vertex][i] == 1 and i not in sequence:
                    dfs(i, sequence.copy())

        for i in range(num_vertices):
            dfs(i, [])

        self.all_sequences = sequences

    def findId(self, listMatrix, id):
        def filter_odd_num(in_num):
            if in_num["name"] == id:
                return True
            else:
                return False

        return list(filter(filter_odd_num, listMatrix))[0]["number"]

    def iter_paths(self, adj, min_length=6, path=None):
        if not path:
            for start_node in range(len(adj)):
                yield from self.iter_paths(adj, min_length, [start_node])
        else:
            if len(path) >= min_length:
                yield path
            if path[-1] in path[:-1]:
                return
            current_node = path[-1]
            for next_node in range(len(adj[current_node])):
                if adj[current_node][next_node] == 1:
                    yield from self.iter_paths(adj, min_length, path + [next_node])

    def allUnique(self, x):
        seen = set()
        return not any(i in seen or seen.add(i) for i in x)

    def all_possible_sequences(self, matrix):
        topologyIds = []
        topologyMatrixNumber = {}
        inc = 0
        for k, v in matrix.items():
            inc += 1
            topologyIds.append({"name": k, "number": inc})
        inc = 0
        for k, v in matrix.items():
            inc += 1
            topologyMatrixNumber[inc] = list(
                map(lambda el: self.findId(topologyIds, el), v)
            )
        self.topologyIds = topologyIds
        adj = []
        matrixSize = matrix.keys().__len__()
        inc = 0
        for k, v in topologyMatrixNumber.items():
            adj.append(list(repeat(0, matrixSize)))
            for el in v:
                adj[inc][el - 1] = 1
            inc += 1
        return self.find_all_sequences(adj)
