"""Module describing the planemo ``shed_diff`` command."""
import shutil
import sys
import tempfile
from xml.sax.saxutils import escape

import click

from planemo import options
from planemo import shed
from planemo.cli import command_function
from planemo.io import captured_io_for_xunit
from planemo.reports.xunit_handler import handle_report_xunit_kwd


@click.command("shed_diff")
@options.shed_read_options()
@click.option(
    "-o", "--output",
    type=click.Path(file_okay=True, resolve_path=True),
    help="Send diff output to specified file.",
    default=None,
)
@click.option(
    '--shed_target_source',
    help="Source Tool Shed to diff against (will ignore local project info"
         " specified). To compare the main Tool Shed against the test, set"
         " this to testtoolshed.",
    default=None,
)
@click.option(
    "--raw",
    is_flag=True,
    help="Do not attempt smart diff of XML to filter out attributes "
         "populated by the Tool Shed.",
)
@options.report_xunit()
@command_function
def cli(ctx, paths, **kwds):
    """diff between local repository and Tool Shed.

    By default, this will produce a diff between this repository and what
    would be uploaded to the Tool Shed with the `shed_upload` command - but
    this command can be made to compare other combinations of repositories.
    Here are some examples

    \b
        $ # diff for this repository and the main Tool Shed
        $ planemo shed_diff
        $ # diff for this repository and the test Tool Shed
        $ planemo shed_diff --shed_target testtoolshed
        $ # diff for the test Tool Shed and main Tool Shed
        $ planemo shed_diff --shed_target_source testtoolshed
        $ # diff for two an explicitly specified repositories (ignores
        $ # current project's shed YAML file.)
        $ planemo shed_diff --owner peterjc --name blast_rbh
            --shed_target_source testtoolshed

    This command will return an exit code of:

    - 0 if there are no detected differences.
    - 1 if there are differences.
    - 2 if the target repository doesn't exist.
    - >200 if there are errors attempting to perform a diff.

    **Warning:** ``shed_diff`` doesn't inspect repository metadata, this
    difference applies only to the file contents of files that would actually be
    uploaded to the repository.
    """

    # In a little bit of cheating, we're defining this variable here to collect
    # a "report" on our shed_diff
    collected_data = {
        'results': {
            'total': 0,
            'errors': 0,
            'failures': 0,
            'skips': 0,
        },
        'suitename': 'shed_diff',
        'tests': [],
    }

    def diff(realized_repository):

        # We create a temporary redirection from kwds's
        # output to our tempfile. This lets us capture the
        # diff and redirect it to their requested location as
        # well as to the XUnit report.
        diff_output = tempfile.NamedTemporaryFile(mode='r')
        user_requested_output = kwds.get('output', None)
        # Replace their output handle with ours
        kwds['output'] = diff_output.name

        captured_io = {}
        with captured_io_for_xunit(kwds, captured_io):
            result = shed.diff_repo(ctx, realized_repository, **kwds)

        # May be extraneous but just want to ensure entire file is written
        # before a copy is made.
        diff_output.flush()
        # Redirect a copy to user_requested_output if they did:
        if user_requested_output is not None:
            shutil.copy(diff_output.name, user_requested_output)
        else:
            with open(diff_output.name, "r") as f:
                sys.stdout.write(f.read())
        # Rewind to the start of the file and read it in its entirety
        diff_output.seek(0)
        diff_output_contents = diff_output.read()
        diff_output.close()

        # Collect data about what happened
        collected_data['results']['total'] += 1
        xunit_case = {
            'name': 'shed-diff',
            'classname': realized_repository.name,
            'time': captured_io["time"],
            'stdout': captured_io["stdout"],
            'stderr': captured_io["stderr"],
        }
        if result >= 200:
            collected_data['results']['errors'] += 1
            xunit_case.update({
                'errorType': 'DiffError',
                'errorMessage': 'Error diffing repositories',
                'errorContent': escape(diff_output_contents),
                'time': captured_io["time"],
            })
        elif result > 2:
            collected_data['results']['failures'] += 1
            xunit_case.update({
                'errorType': 'PlanemoDiffError',
                'errorMessage': 'Planemo error diffing repositories',
                'errorContent': escape(diff_output_contents),
            })
        elif result == 2:
            collected_data['results']['failures'] += 1
            xunit_case.update({
                'errorType': 'RepoDoesNotExist',
                'errorMessage': 'Target Repository does not exist',
                'errorContent': escape(diff_output_contents),
            })
        elif result == 1:
            collected_data['results']['failures'] += 1
            xunit_case.update({
                'errorType': 'Different',
                'errorMessage': 'Repository is different',
                'errorContent': escape(diff_output_contents),
            })

        # Append our xunit test case
        collected_data['tests'].append(xunit_case)

        return result

    exit_code = shed.for_each_repository(ctx, diff, paths, **kwds)

    handle_report_xunit_kwd(kwds, collected_data)

    sys.exit(exit_code)
