import uuid
from typing import Dict, Iterable, Optional, Tuple

from tdm.abstract.datamodel import AbstractDocumentFactory, AbstractDomain, AbstractFact, AbstractNode, \
    AbstractNodeLink, FactStatus, TalismanDocument
from tdm.datamodel.common import TypedIdsContainer
from tdm.datamodel.domain import get_default_domain
from tdm.datamodel.domain.types import DocumentType
from tdm.datamodel.facts import ConceptFact
from tdm.datamodel.facts.concept import ConceptValue
from ._impl import TalismanDocumentImpl
from ._structure import NodesStructure


class TalismanDocumentFactory(AbstractDocumentFactory):

    def __init__(self, domain: Optional[AbstractDomain] = None):
        self._domain = domain if domain is not None else get_default_domain()

    def create_document(self, *, id_: Optional[str] = None, doc_type: Optional[str] = None) -> TalismanDocument:
        doc = TalismanDocumentImpl(
            id2view={},
            dependencies={},
            structure=NodesStructure(),
            containers={
                AbstractNode: TypedIdsContainer(AbstractNode, ()),
                AbstractNodeLink: TypedIdsContainer(AbstractNodeLink, ()),
                AbstractFact: TypedIdsContainer(AbstractFact, ())
            },
            metadata=None,
            id_=id_ or self.generate_id(),
            domain=self._domain
        )
        if doc_type is not None:
            if self._domain is not None:
                doc_type = self._domain.get_type(doc_type)
                if not isinstance(doc_type, DocumentType):
                    raise ValueError
            return doc.with_facts([ConceptFact(FactStatus.APPROVED, doc_type, ConceptValue(doc.id), id=doc.id)])
        return doc

    def construct(
            self,
            content: Iterable[AbstractNode] = (),
            structure: Dict[str, Tuple[str, ...]] = None,
            root: Optional[str] = None,
            node_links: Iterable[AbstractNodeLink] = (),
            facts: Iterable[AbstractFact] = (),
            *, id_: Optional[str] = None
    ) -> TalismanDocument:
        doc: TalismanDocumentImpl = self.create_document(id_=id_)
        doc = doc.with_elements((*content, *node_links, *facts)) \
            .with_structure(structure) \
            .with_main_root(root)
        return doc

    @staticmethod
    def generate_id():
        return str(uuid.uuid4())
