import sys

from dagster import check
from dagster.config.validate import validate_config_from_snap
from dagster.core.host_representation import ExternalPipeline, PipelineSelector, RepositorySelector
from dagster.core.workspace.context import BaseWorkspaceRequestContext
from dagster.utils.error import serializable_error_info_from_exc_info
from graphql.execution.base import ResolveInfo

from .utils import UserFacingGraphQLError, capture_error


def get_full_external_pipeline_or_raise(graphene_info, selector):
    from ..schema.errors import GraphenePipelineNotFoundError

    check.inst_param(graphene_info, "graphene_info", ResolveInfo)
    check.inst_param(selector, "selector", PipelineSelector)

    if not graphene_info.context.has_external_pipeline(selector):
        raise UserFacingGraphQLError(GraphenePipelineNotFoundError(selector=selector))

    return graphene_info.context.get_full_external_pipeline(selector)


def get_external_pipeline_or_raise(graphene_info, selector):
    from ..schema.pipelines.pipeline_errors import GrapheneInvalidSubsetError
    from ..schema.pipelines.pipeline import GraphenePipeline

    check.inst_param(graphene_info, "graphene_info", ResolveInfo)
    check.inst_param(selector, "selector", PipelineSelector)

    full_pipeline = get_full_external_pipeline_or_raise(graphene_info, selector)

    if selector.solid_selection is None:
        return full_pipeline

    for solid_name in selector.solid_selection:
        if not full_pipeline.has_solid_invocation(solid_name):
            raise UserFacingGraphQLError(
                GrapheneInvalidSubsetError(
                    message='Solid "{solid_name}" does not exist in "{pipeline_name}"'.format(
                        solid_name=solid_name, pipeline_name=selector.pipeline_name
                    ),
                    pipeline=GraphenePipeline(full_pipeline),
                )
            )

    return get_subset_external_pipeline(graphene_info.context, selector)


def get_subset_external_pipeline(context, selector):
    from ..schema.pipelines.pipeline_errors import GrapheneInvalidSubsetError
    from ..schema.pipelines.pipeline import GraphenePipeline

    check.inst_param(selector, "selector", PipelineSelector)

    repository_location = context.get_repository_location(selector.location_name)

    try:
        external_pipeline = repository_location.get_external_pipeline(selector)
    except Exception:
        error_info = serializable_error_info_from_exc_info(sys.exc_info())
        raise UserFacingGraphQLError(
            GrapheneInvalidSubsetError(
                message="{message}{cause_message}".format(
                    message=error_info.message,
                    cause_message="\n{}".format(error_info.cause.message)
                    if error_info.cause
                    else "",
                ),
                pipeline=GraphenePipeline(context.get_full_external_pipeline(selector)),
            )
        )

    return external_pipeline


def ensure_valid_config(external_pipeline, mode, run_config):
    from ..schema.pipelines.config import GrapheneRunConfigValidationInvalid

    check.inst_param(external_pipeline, "external_pipeline", ExternalPipeline)
    check.opt_str_param(mode, "mode")
    # do not type check run_config so that validate_config_from_snap throws

    validated_config = validate_config_from_snap(
        config_schema_snapshot=external_pipeline.config_schema_snapshot,
        config_type_key=external_pipeline.root_config_key_for_mode(mode),
        config_value=run_config,
    )

    if not validated_config.success:

        raise UserFacingGraphQLError(
            GrapheneRunConfigValidationInvalid.for_validation_errors(
                external_pipeline, validated_config.errors
            )
        )

    return validated_config


def get_external_execution_plan_or_raise(
    graphene_info,
    external_pipeline,
    mode,
    run_config,
    step_keys_to_execute,
    known_state,
):

    return graphene_info.context.get_external_execution_plan(
        external_pipeline=external_pipeline,
        run_config=run_config,
        mode=mode,
        step_keys_to_execute=step_keys_to_execute,
        known_state=known_state,
    )


@capture_error
def fetch_repositories(graphene_info):
    from ..schema.external import GrapheneRepository, GrapheneRepositoryConnection

    check.inst_param(graphene_info, "graphene_info", ResolveInfo)
    return GrapheneRepositoryConnection(
        nodes=[
            GrapheneRepository(repository=repository, repository_location=location)
            for location in graphene_info.context.repository_locations
            for repository in location.get_repositories().values()
        ]
    )


@capture_error
def fetch_repository(graphene_info, repository_selector):
    from ..schema.errors import GrapheneRepositoryNotFoundError
    from ..schema.external import GrapheneRepository

    check.inst_param(graphene_info, "graphene_info", ResolveInfo)
    check.inst_param(repository_selector, "repository_selector", RepositorySelector)

    if graphene_info.context.has_repository_location(repository_selector.location_name):
        repo_loc = graphene_info.context.get_repository_location(repository_selector.location_name)
        if repo_loc.has_repository(repository_selector.repository_name):
            return GrapheneRepository(
                repository=repo_loc.get_repository(repository_selector.repository_name),
                repository_location=repo_loc,
            )

    return GrapheneRepositoryNotFoundError(
        repository_selector.location_name, repository_selector.repository_name
    )


@capture_error
def fetch_workspace(workspace_request_context):
    from ..schema.external import GrapheneWorkspace, GrapheneWorkspaceLocationEntry

    check.inst_param(
        workspace_request_context, "workspace_request_context", BaseWorkspaceRequestContext
    )

    nodes = [
        GrapheneWorkspaceLocationEntry(entry)
        for entry in workspace_request_context.get_workspace_snapshot().values()
    ]

    return GrapheneWorkspace(locationEntries=nodes)

[((16, 4, 16, 65), 'dagster.check.inst_param', 'check.inst_param', ({(16, 21, 16, 34): 'graphene_info', (16, 36, 16, 51): '"""graphene_info"""', (16, 53, 16, 64): 'ResolveInfo'}, {}), "(graphene_info, 'graphene_info', ResolveInfo)", False, 'from dagster import check\n'), ((17, 4, 17, 60), 'dagster.check.inst_param', 'check.inst_param', ({(17, 21, 17, 29): 'selector', (17, 31, 17, 41): '"""selector"""', (17, 43, 17, 59): 'PipelineSelector'}, {}), "(selector, 'selector', PipelineSelector)", False, 'from dagster import check\n'), ((29, 4, 29, 65), 'dagster.check.inst_param', 'check.inst_param', ({(29, 21, 29, 34): 'graphene_info', (29, 36, 29, 51): '"""graphene_info"""', (29, 53, 29, 64): 'ResolveInfo'}, {}), "(graphene_info, 'graphene_info', ResolveInfo)", False, 'from dagster import check\n'), ((30, 4, 30, 60), 'dagster.check.inst_param', 'check.inst_param', ({(30, 21, 30, 29): 'selector', (30, 31, 30, 41): '"""selector"""', (30, 43, 30, 59): 'PipelineSelector'}, {}), "(selector, 'selector', PipelineSelector)", False, 'from dagster import check\n'), ((55, 4, 55, 60), 'dagster.check.inst_param', 'check.inst_param', ({(55, 21, 55, 29): 'selector', (55, 31, 55, 41): '"""selector"""', (55, 43, 55, 59): 'PipelineSelector'}, {}), "(selector, 'selector', PipelineSelector)", False, 'from dagster import check\n'), ((81, 4, 81, 78), 'dagster.check.inst_param', 'check.inst_param', ({(81, 21, 81, 38): 'external_pipeline', (81, 40, 81, 59): '"""external_pipeline"""', (81, 61, 81, 77): 'ExternalPipeline'}, {}), "(external_pipeline, 'external_pipeline', ExternalPipeline)", False, 'from dagster import check\n'), ((82, 4, 82, 37), 'dagster.check.opt_str_param', 'check.opt_str_param', ({(82, 24, 82, 28): 'mode', (82, 30, 82, 36): '"""mode"""'}, {}), "(mode, 'mode')", False, 'from dagster import check\n'), ((124, 4, 124, 65), 'dagster.check.inst_param', 'check.inst_param', ({(124, 21, 124, 34): 'graphene_info', (124, 36, 124, 51): '"""graphene_info"""', (124, 53, 124, 64): 'ResolveInfo'}, {}), "(graphene_info, 'graphene_info', ResolveInfo)", False, 'from dagster import check\n'), ((139, 4, 139, 65), 'dagster.check.inst_param', 'check.inst_param', ({(139, 21, 139, 34): 'graphene_info', (139, 36, 139, 51): '"""graphene_info"""', (139, 53, 139, 64): 'ResolveInfo'}, {}), "(graphene_info, 'graphene_info', ResolveInfo)", False, 'from dagster import check\n'), ((140, 4, 140, 84), 'dagster.check.inst_param', 'check.inst_param', ({(140, 21, 140, 40): 'repository_selector', (140, 42, 140, 63): '"""repository_selector"""', (140, 65, 140, 83): 'RepositorySelector'}, {}), "(repository_selector, 'repository_selector', RepositorySelector\n    )", False, 'from dagster import check\n'), ((159, 4, 161, 5), 'dagster.check.inst_param', 'check.inst_param', ({(160, 8, 160, 33): 'workspace_request_context', (160, 35, 160, 62): '"""workspace_request_context"""', (160, 64, 160, 91): 'BaseWorkspaceRequestContext'}, {}), "(workspace_request_context, 'workspace_request_context',\n    BaseWorkspaceRequestContext)", False, 'from dagster import check\n'), ((62, 59, 62, 73), 'sys.exc_info', 'sys.exc_info', ({}, {}), '()', False, 'import sys\n')]