"""
Class containing error conditions that are exposed to the user.
"""
import traceback
from typing import IO, Optional
from urllib.parse import quote

import click


class ConfigException(click.ClickException):
    """
    Exception class when configuration file fails checks.
    """


class UserException(click.ClickException):
    """
    Base class for all exceptions that need to be surfaced to the user. Typically, we will display the exception
    message to user and return the error code from CLI process
    """

    exit_code = 1

    def __init__(self, message, wrapped_from=None):
        self.wrapped_from = wrapped_from

        click.ClickException.__init__(self, message)


class UnhandledException(click.ClickException):
    """
    Exception class to re-wrap any exception that is not a UserException.
    Typically this means there is a bug in SAM CLI.
    """

    GH_ISSUE_SEARCH_URL = "https://github.com/aws/aws-sam-cli/issues?q=is%3Aissue+is%3Aopen+{title}"
    GH_BUG_REPORT_URL = "https://github.com/aws/aws-sam-cli/issues/new?template=Bug_report.md&title={title}"
    # NOTE (hawflau): actual exitcode is 1 to not break existing behavior. Only report 255 to telemetry
    exit_code = 1

    def __init__(self, command: str, exception: Exception) -> None:
        self._command = command
        self._exception = exception
        self.__traceback__ = self._exception.__traceback__

        click.ClickException.__init__(self, type(exception).__name__)

    def show(self, file: Optional[IO] = None) -> None:
        """Overriding show to customize printing stack trace and message"""
        if file is None:
            file = click._compat.get_text_stderr()  # pylint: disable=protected-access

        tb = "".join(traceback.format_tb(self.__traceback__))
        click.echo(f"\nError: {self._exception}", file=file, err=True)
        click.echo(f"Traceback:\n{tb}", file=file, err=True)

        encoded_title = quote(f"Bug: {self._command} - {type(self._exception).__name__}")
        lookup_url = self.GH_ISSUE_SEARCH_URL.format(title=encoded_title)
        create_issue_url = self.GH_BUG_REPORT_URL.format(title=encoded_title)
        msg = (
            f'An unexpected error was encountered while executing "{self._command}".\n'
            "Search for an existing issue:\n"
            f"{lookup_url}\n"
            "Or create a bug report:\n"
            f"{create_issue_url}"
        )
        click.secho(msg, file=file, err=True, fg="yellow")


class CredentialsError(UserException):
    """
    Exception class when credentials that have been passed are invalid.
    """


class SDKError(UserException):
    """
    Exception class when there are generic Boto Errors.
    """


class SchemasApiException(UserException):
    """
    Exception class to wrap all Schemas APIs exceptions.
    """


class RegionError(UserException):
    """
    Exception class when no valid region is passed to a client.
    """


class AppTemplateUpdateException(UserException):
    """
    Exception class when updates to app templates for init enters an unstable state.
    """


class LambdaImagesTemplateException(UserException):
    """
    Exception class when multiple Lambda Image app templates are found for any runtime
    """


class ContainersInitializationException(UserException):
    """
    Exception class when SAM is not able to initialize any of the lambda functions containers
    """


class PipelineTemplateCloneException(UserException):
    """
    Exception class when unable to download pipeline templates from a Git repository during `sam pipeline init`
    """


class AppPipelineTemplateManifestException(UserException):
    """
    Exception class when SAM is not able to parse the "manifest.yaml" file located in the SAM pipeline templates
    Git repo: "github.com/aws/aws-sam-cli-pipeline-init-templates.git
    """


class AppPipelineTemplateMetadataException(UserException):
    """
    Exception class when SAM is not able to parse the "metadata.json" file located in the SAM pipeline templates
    """


class InvalidInitOptionException(UserException):
    """
    Exception class when user provides wrong options
    """


class InvalidImageException(UserException):
    """
    Value provided to --build-image or --invoke-image is invalid URI
    """


class InvalidStackNameException(UserException):
    """
    Value provided to --stack-name is invalid
    """


class LinterRuleMatchedException(UserException):
    """
    The linter matched a rule meaning that the template linting failed
    """
