import ntsbuildtools.exceptions


class TeamsCard:
    """TeamsCard represents a Microsoft Teams MessageCard (Legacy). The card generated by the to_json() method
    should be used as the HTTP POST payload to a 'Incoming Webhook Connector'.
    """

    def __init__(self):
        self.potential_actions = []
        self.sections = []
        self.title = None
        # By default, send almost-empty text (otherwise the API throws a fit)
        self.text = ' '
        self.section_index = 0

    def add_section(self, text=None):
        new_section = TeamsCardSection()
        self.sections.append(new_section)
        if text:
            new_section.add_text(text)
        return new_section

    def add_button(self, text, url):
        """[summary]

        Args:
            text ([type]): [description]
            url ([type]): [description]

        Returns:
            [type]: [description]
        """
        self.potential_actions.append(
            {
                "@type": "OpenUri",
                "name": text,
                "targets": [{
                    "os": "default",
                    "uri": url
                }]
            }
        )
        return self

    def set_text(self, text):
        # Set to non-empty string no matter what (see e2e test case: test_build_status_card_success_with_empty_file)
        self.text = text if text else ' '
        return self

    def set_title(self, title):
        self.title = title
        return self

    def to_json(self):
        return {
            "@context": "https://schema.org/extensions",
            "@type": "MessageCard",
            "themeColor": "0072C6",
            "title": self.title,
            "text": self.text,
            "sections": [section.to_json() for section in self.sections],
            "potentialAction": self.potential_actions
        }


class TeamsCardSection:
    """TeamsCardSection is a section within a Teams card. It can contain 'facts', 'text', as well many other data
    as specified in: https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#section-fields
    """

    def __init__(self):
        self.facts = []
        self.text_items = []
        self.start_group = False

    def set_start_group(self):
        self.start_group = True
        return self

    def add_fact(self, label, text):
        self.facts.append({
            "name": label,
            "value": text,
        })
        return self

    def add_text(self, text: str):
        self.text_items.append(text)
        return self

    def to_json(self):
        if len(self.text_items) > 0:
            return {"start_group": self.start_group, "facts": self.facts, "text": "\n".join(self.text_items)}
        return {"start_group": self.start_group, "facts": self.facts}


class TeamsCardTemplate(TeamsCard):
    def __init__(self, required_args, optional_args, args):
        """TeamsCardTemplate is an abstract extension of the TeamsCard class that should be used as 
        the base class when creating new 'preformatted TeamsCards' classes.

        Args:
            required_args (set[str]): Set of arguments that are required to create this card.
            optional_args (set[str]): Set of arguments that are optional when creating this card.
            args (Args): The collection of arguments (implementors should pass through args from).  generally a parsearg.Namespace object. 

        A simple code example for creating a preformatted TeamsCard is below:

        ```
            class HelloWorldCard(TeamsCardTemplate):
                def __init__(self, args):
                    required_args = {'name'}
                    optional_args = {'greeting'}
                    super.__init__(required_args, optional_args, args)

                def build(self, args):
                    self.set_title(f'Hello, {args.name}!')
                    if args.greeting: # overwrite title if a optional 'greeting' argument is given.
                        self.set_title(f'{args.greeting}, {args.name}!')
        ```

        """
        super().__init__()
        self.required_args = required_args
        self.optional_args = optional_args
        # provided_args excludes arguments that have value 'None'
        all_args = vars(args).items()
        self.provided_args = set(
            [None if val is None else arg for arg, val in all_args])
        self._check_args()
        self.build(args)

    def build(self, args):
        """Factory Method that should be overwritten by classes that extend TeamsCardTemplate.

        Args:
            args: Arguments that will be used by the card-build process.
        """
        # The minimum viable card doesn't require any arguments, but it'll still take the title if given
        if hasattr(args, 'title'):
            self.set_title(args.title)
        else:
            self.set_title("[DEFAULT_CARD_TITLE]")

    def _check_args(self):
        """Check that all the arguments required by the _build method are available.

        Raises:
            ntsbuildtools.exceptions.BTTeamsCardFactoryError: If any required 
                arguments are missing.
        """
        if not self.required_args.issubset(self.provided_args):
            raise ntsbuildtools.exceptions.BTTeamsCardFactoryError(
                self._build_missing_args_message())

    # def _build_missing_args_message(self):
    #     message = ['Missing required arguments: ']
    #     # Add the list of arguments that were missing
    #     message.append(', '.join( self.required_args - self.provided_args ) + '.' )
    #     if len(self.optional_args) > 0:
    #         message.append('\n    [INFO] Optional arguments: ')
    #         message.append(', '.join( self.optional_args - self.provided_args ) + '.')
    #     return "".join(message)

    def _build_missing_args_message(self):
        my_type = type(self).__name__ 
        msg = f'Missing required arguments when creating {my_type}'
        msg = f'{msg}: {self._infer_missing_required_arguments()}.'
        # Add the list of arguments that were missing
        if self._infer_missing_optional_arguments():
            msg = f'{msg}\nOptional arguments for {my_type}'
            msg = f'{msg}: {self._infer_missing_optional_arguments()}.'
            msg = f"{msg} Was '{my_type}' the type of card you meant to build?"
            msg = f'{msg} If not, review the TeamsCardFactory.build() method for that'
            msg = f'{msg} card type to understand how this card type was chosen (based'
            msg = f'{msg} on the arguments you provided).'
        return msg

    def _infer_missing_required_arguments(self):
        return TeamsCardTemplate._infer_missing_arguments(
            self.required_args, self.provided_args)

    def _infer_missing_optional_arguments(self):
        return TeamsCardTemplate._infer_missing_arguments(
            self.optional_args, self.provided_args)

    @staticmethod
    def _infer_missing_arguments(expected, provided) -> str:
        missing = expected - provided
        if len(missing) == 0:
            return None
        missing_args = [f"--{arg.replace('_', '-')}" for arg in missing]
        return ', '.join(missing_args)
