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='./dbt_projects/jaffle_shop/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='./dbt_projects/jaffle_shop/target')
            >>> model = project.get_model('customers')
            >>> model.name
            'customers'

        """

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