from typing import List, Dict, Any, Optional

import pluggy
from git import Repo, Tag
from releaseherald.configuration import Configuration
from releaseherald.plugins import CommitInfo
from releaseherald.plugins.interface import (
    MutableProxy,
    VersionNews,
    News,
    Output,
    CommandOptions,
)

hookspec = pluggy.HookspecMarker("releaseherald")


@hookspec
def process_config(config: Configuration):
    """
    Called as the first callback to the plugin. It can use this to
    initiate itself based in the configuration read from the config file.
    If also has a chance to change the configuration. It can for example
    parse and validate it's own sub configuration and replace the dict in `config`
    with a more manageable object.

    Args:
        config: The configuration
    """
    pass


@hookspec
def get_command_options(command: str) -> Optional[CommandOptions]:
    """
    This callback give chance to a plugin to add commandline options to
    various commands. It is called with the name of the command.

    Args:
        command: something

    Returns:
     collection of `click.Options` that are added to the cli command
        and a callable that can set the configured value as the default for the
        cli option"""
    pass


@hookspec
def on_start_command(command: str, kwargs: Dict[str, Any]):
    """
    Called before a cli command start to execute.
    Args:
        command: the name of the command
        kwargs: the parameters the command called with
    """
    pass


@hookspec
def process_tags(repo: Repo, tags: List[Tag]):
    """

    Args:
        repo: the git repository
        tags:
            List of tags releaseherald consider as the versions
            it needs to collect newsfragments. The plugin is free
            to manipulate the list of tags complex filtering can be
            implemented here.
    """
    pass


@hookspec
def process_commits(repo: Repo, tags: List[Tag], commits: List[CommitInfo]):
    """
    The aim of this hook is to collect the list of commits based on the tags.
    The plugin supposed to modify the `commits` list. The default plugin, just
    turns the tags into [CommitInfo][releaseherald.plugins.interface.CommitInfo].

    Args:
        repo: the git repository
        tags: the tags collected by [process_tags][releaseherald.plugins.hookspecs.process_tags]
        commits: Info about each commits

    """
    pass


@hookspec
def get_news_between_commits(
    repo: Repo,
    commit_from: CommitInfo,
    commit_to: CommitInfo,
    news: List[News],
):
    """
    In this hook the plugin can alter the collected `news` between the two commits. It is
    called for every consecutive commit pairs processed by
    [process_commits][releaseherald.plugins.hookspecs.process_commits].

    Args:
        repo: the git repository
        commit_from: The earlier commit
        commit_to: The later commit
        news: The list of news that previous plugins collected, can be altered by the plugin

    """
    pass


@hookspec
def get_version_news(
    repo: Repo,
    commit_from: CommitInfo,
    commit_to: CommitInfo,
    news: List[News],
    version_news: MutableProxy[VersionNews],
):
    """
    In this hook plugins can produce a wrapper around the the list of news that represent a
    version in releaseherald datastructure. Called for every consecutive commit pairs processed by
    [process_commits][releaseherald.plugins.hookspecs.process_commits] with the news processed by
    [get_news_between_commits][releaseherald.plugins.hookspecs.get_news_between_commits] for the
    same two commits.

    Args:
        repo: the git repository
        commit_from: The earlier commit
        commit_to: The later commit
        news: The list of news collected by
              [get_news_between_commits][releaseherald.plugins.hookspecs.get_news_between_commits]
        version_news: The version news representing a version with the changes between two commits
    """
    pass


@hookspec
def process_version_news(version_news: List[VersionNews]):
    """
    This hook give a chance for the plugin to alter the list of versions.

    Args:
        version_news: All the version/news collected so far
    """
    pass


@hookspec
def generate_output(version_news: List[VersionNews], output: MutableProxy[Output]):
    """
    The plugin can generate an output in memory in any kind of format it want. It also has a
    chance to alter or replace an output generated by any previous plugins
    Args:
        version_news:
            All the version/news collected and processed by
            [process_version_news][releaseherald.plugins.hookspecs.process_version_news]
        output: Output in plugin specific format
    """
    pass


@hookspec
def write_output(output: Output):
    """
    The plugin should do its final output step here. Write to file, to stdout or send a mail,
    upload to some service whatever desired.
    Args:
        output: the output from [generate_output][releaseherald.plugins.hookspecs.generate_output]

    """
    pass
