from typing import Dict, List, Union
from pydantic import BaseModel, Field

from artefacts.mixins import ContextReader, Artifact
from artefacts.nodes import BaseNodeReference, BaseNode
from artefacts.shared import Metadata


# https://schemas.getdbt.com/dbt/manifest/v3/index.html#nodes
class ManifestNode(BaseNode, BaseModel):
    class Config:
        extra = "ignore"

    resource_type: str
    name: str


class ManifestNodeReference(BaseNodeReference):
    pass


# https://schemas.getdbt.com/dbt/manifest/v3/index.html#docs_additionalProperties
class ManifestCatalogNode(BaseNode, BaseModel):
    class Config:
        extra = "ignore"

    unique_id: str
    package_name: str
    root_path: str
    path: str
    original_file_path: str
    name: str
    block_contents: str


# https://schemas.getdbt.com/dbt/manifest/v3/index.html
class Manifest(Artifact, ContextReader, BaseModel):
    """
    Manifests are artifacts generated by any dbt command that compiles the dbt project,
    including `dbt compile`, `dbt build`, `dbt docs generate`, etc. The manifest
    contains information about all nodes in the project, including models, tests,
    seeds, etc.

    Examples:

        >>> from artefacts import Project
        >>> project = Project(target='./target')
        >>> print(project.manifest)
        <Manifest v3>

    Attributes:

        metadata (`Metadata`):
            The [`Metadata`][artefacts.shared.Metadata] associated with the artifact

        source_nodes (`dict`):
            The source_nodes defined in the dbt project

        nodes (`dict`):
            All of the nodes defined in the dbt project, ie models, tests, seeds, etc

        child_map (`dict`):
            A mapping to look up all the first-level nodes that depend on a given node.

        parent_map (`dict`):
            A mapping to look up all the first-level nodes that a given node depends on.

        macros (`dict`):
            The macros defined in the dbt project

        docs (`dict`):
            The docs defined in the dbt project

        exposures (`dict`):
            The exposures defined in the dbt project

        selectors (`dict`):
                 The selectors defined in the dbt project

        disabled (`List`):
            A list of the disabled nodes in the project

    """

    class Config:
        arbitrary_types_allowed = True
        extra = "ignore"

    metadata: Metadata
    source_nodes: Dict[ManifestNodeReference, ManifestNode] = Field(
        ..., alias="sources"
    )
    nodes: Dict[ManifestNodeReference, ManifestNode]
    child_map: Dict[ManifestNodeReference, List[ManifestNodeReference]]
    parent_map: Dict[ManifestNodeReference, List[ManifestNodeReference]]
    macros: Dict[ManifestNodeReference, ManifestNode]
    docs: Dict[ManifestNodeReference, ManifestCatalogNode]
    exposures: Dict[ManifestNodeReference, ManifestNode]
    selectors: dict
    disabled: Union[None, List[ManifestNode]]
