from typing import Union

from artefacts.mixins import ContextReader
from artefacts.catalog import Catalog
from artefacts.manifest import Manifest
from artefacts.run_results import RunResults
from artefacts.sources import Sources


class Project(ContextReader):
    """
    Examples:

        >>> from artefacts import Project
        >>> project = Project(target='./target')

    Attributes:

        manifest (Manifest):
            The project's compiled [`Manifest`][artefacts.manifest.Manifest], which
            includes information about models, tests, sources, seeds, and other
            data about project.

        catalog (Catalog):
            The project's compiled [`Catalog`][artefacts.catalog.Catalog], which
            contains information about the model's actual columns as defined in your
            data warehouse's information_schema tables. Requires that `dbt docs
            generate` was ran before initializing the object, ie before using
            `Project()`.

        run_results (RunResults):
            The project' compiled [`RunResults`][artefacts.run_results.RunResults]
            artifact, which contains details of the results from any `build`, `run`,
            `test` commands.

        sources (Sources):
            The projects compiled [`Sources`][artefacts.sources.Sources] artifact,
            which contains details about the freshness of the project's defined
            sources, as generated by `dbt source snapshot-freshness`.

        models (list):
            A list of the project's compiled models.

        tests (list):
            A list of the project's compiled tests.

        seeds (list):
            A list of the project's compiled seeds.
    """

    manifest: Manifest
    catalog: Union[Catalog, None]
    run_results: Union[RunResults, None]
    sources: Union[Sources, None]

    def __init__(self, target="./target"):
        self.target = target
        self.context.load("manifest", target=self.target, allow_missing=False)
        self.context.parse("manifest", parser=Manifest, allow_missing=False)
        self.context.load("catalog", target=self.target, allow_missing=True)
        self.context.parse("catalog", parser=Catalog, allow_missing=True)
        self.context.load("run_results", target=self.target, allow_missing=True)
        self.context.parse("run_results", parser=RunResults, allow_missing=True)
        self.context.load("sources", target=self.target, allow_missing=True)
        self.context.parse("sources", parser=Sources, allow_missing=True)

    @property
    def models(self):
        return [v for k, v in self.manifest.nodes.items() if v.resource_type == "model"]

    @property
    def tests(self):
        return [v for k, v in self.manifest.nodes.items() if v.resource_type == "test"]

    @property
    def seeds(self):
        return [v for k, v in self.manifest.nodes.items() if v.resource_type == "seed"]

    def get_model(self, name: str):
        """
        Get a model by name.

        Examples:

            >>> from artefacts import Project
            >>> project = Project(target='./target')
            >>> model = project.get_model('customers')
            >>> model.name
            'customers'

        """

        for model in self.models:
            if model.name == name:
                return model
