"""
Functionality for dealing with tool errors.
"""

import string

import markupsafe

from galaxy import (
    model,
    util,
)
from galaxy.security.validate_user_input import validate_email_str
from galaxy.util import unicodify

error_report_template = """
GALAXY TOOL ERROR REPORT
------------------------

This error report was sent from the Galaxy instance hosted on the server
"${host}"
-----------------------------------------------------------------------------
This is in reference to dataset id ${dataset_id_encoded} from history id ${history_id_encoded}
-----------------------------------------------------------------------------
You should be able to view the history containing the related history item (${hda_id_encoded})

${hid}: ${history_item_name}

by logging in as a Galaxy admin user to the Galaxy instance referenced above
and pointing your browser to the following link.

${history_view_link}
-----------------------------------------------------------------------------
The user ${email_str} provided the following information:

${message}
-----------------------------------------------------------------------------
info url: ${hda_show_params_link}
job id: ${job_id_encoded}
tool id: ${job_tool_id}
tool version: ${tool_version}
job pid or drm id: ${job_runner_external_id}
job tool version: ${job_tool_version}
-----------------------------------------------------------------------------
job command line:
${job_command_line}
-----------------------------------------------------------------------------
job stderr:
${job_stderr}
-----------------------------------------------------------------------------
job stdout:
${job_stdout}
-----------------------------------------------------------------------------
job info:
${job_info}
-----------------------------------------------------------------------------
job traceback:
${job_traceback}
-----------------------------------------------------------------------------
(This is an automated message).
"""

error_report_template_html = """
<html>
    <body>
<h1>Galaxy Tool Error Report</h1>
<span class="sub"><i>from</i> <span style="font-family: monospace;"><a href="${host}">${host}</a></span>

<h3>Error Localization</h3>
<table style="margin:1em">
    <tbody>
        <tr><td>Dataset</td><td><a href="${hda_show_params_link}">${dataset_id_encoded}</a></td></tr>
        <tr style="background-color: #f2f2f2"><td>History</td><td><a href="${history_view_link}">${history_id_encoded}</a></td></tr>
        <tr><td>Failed Job</td><td>${hid}: ${history_item_name} (${hda_id_encoded})</td></tr>
    </tbody>
</table>

<h3>User Provided Information</h3>

The user <span style="font-family: monospace;">${email_str}</span> provided the following information:

<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${message}
</pre>


<h3>Detailed Job Information</h3>

Job environment and execution information is available at the job <a href="${hda_show_params_link}">info page</a>.

<table style="margin:1em">
    <tbody>
        <tr><td>Job ID</td><td>${job_id_encoded}</td></tr>
        <tr style="background-color: #f2f2f2"><td>Tool ID</td><td>${job_tool_id}</td></tr>
        <tr><td>Tool Version</td><td>${tool_version}</td></tr>
        <tr style="background-color: #f2f2f2"><td>Job PID or DRM id</td><td>${job_runner_external_id}</td></tr>
        <tr><td>Job Tool Version</td><td>${job_tool_version}</td></tr>
    </tbody>
</table>

<h3>Job Execution and Failure Information</h3>

<h4>Command Line</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_command_line}
</pre>

<h4>stderr</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_stderr}
</pre>

<h4>stdout</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_stdout}
</pre>

<h4>Job Information</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_info}
</pre>

<h4>Job Traceback</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_traceback}
</pre>

This is an automated message. Do not reply to this address.
</body></html>
"""


class ErrorReporter:
    def __init__(self, hda, app):
        # Get the dataset
        sa_session = app.model.context
        if not isinstance(hda, model.HistoryDatasetAssociation):
            hda_id = hda
            try:
                hda = sa_session.get(model.HistoryDatasetAssociation, hda_id)
                assert hda is not None, ValueError("No HDA yet")
            except Exception:
                hda = sa_session.get(model.HistoryDatasetAssociation, app.security.decode_id(hda_id))
        assert isinstance(hda, model.HistoryDatasetAssociation), ValueError(f"Bad value provided for HDA ({hda}).")
        self.hda = hda
        # Get the associated job
        self.job = hda.creating_job
        self.app = app
        self.tool_id = self.job.tool_id
        self.report = None

    def _can_access_dataset(self, user):
        if user:
            roles = user.all_roles()
        else:
            roles = []
        return self.app.security_agent.can_access_dataset(roles, self.hda.dataset)

    def create_report(self, user, email="", message="", redact_user_details_in_bugreport=False, **kwd):
        hda = self.hda
        job = self.job
        host = self.app.url_for("/", qualified=True)
        history_id_encoded = self.app.security.encode_id(hda.history_id)
        history_view_link = self.app.url_for("/histories/view", id=history_id_encoded, qualified=True)
        hda_id_encoded = self.app.security.encode_id(hda.id)
        hda_show_params_link = self.app.url_for(
            controller="dataset", action="details", dataset_id=hda_id_encoded, qualified=True
        )
        # Build the email message
        if redact_user_details_in_bugreport:
            # This is sub-optimal but it is hard to solve fully. This affects
            # the GitHub posting method more than the traditional email plugin.
            # There is no way around CCing the person with the traditional
            # email bug report plugin, however with the GitHub plugin we can
            # submit to GitHub without putting the email in the bug report.
            #
            # A secondary system with access to the GitHub issue and access to
            # the Galaxy database can shuttle email back and forth between
            # GitHub comments and user-emails.
            # Thus preventing issue helpers from every knowing the identity of
            # the bug reporter (and preventing information about the bug
            # reporter from leaving the EU until it hits email directly to the
            # user.)
            email_str = "redacted"
            if user:
                email_str += f" (user: {user.id})"
        else:
            if user:
                email_str = f"'{user.email}'"
                if email and user.email != email:
                    email_str += f" (providing preferred contact email '{email}')"
            else:
                email_str = "'%s'" % (email or "anonymous")

        report_variables = dict(
            host=host,
            dataset_id_encoded=self.app.security.encode_id(hda.dataset_id),
            history_id_encoded=history_id_encoded,
            hda_id_encoded=hda_id_encoded,
            hid=hda.hid,
            history_item_name=hda.get_display_name(),
            history_view_link=history_view_link,
            hda_show_params_link=hda_show_params_link,
            job_id_encoded=self.app.security.encode_id(job.id),
            tool_version=job.tool_version,
            job_tool_id=job.tool_id,
            job_tool_version=hda.tool_version,
            job_runner_external_id=job.job_runner_external_id,
            job_command_line=job.command_line,
            job_stderr=util.unicodify(job.stderr),
            job_stdout=util.unicodify(job.stdout),
            job_info=util.unicodify(job.info),
            job_traceback=util.unicodify(job.traceback),
            email_str=email_str,
            message=util.unicodify(message),
        )

        self.report = string.Template(error_report_template).safe_substitute(report_variables)

        # Escape all of the content  for use in the HTML report
        for parameter in report_variables.keys():
            if report_variables[parameter] is not None:
                report_variables[parameter] = markupsafe.escape(unicodify(report_variables[parameter]))

        self.html_report = string.Template(error_report_template_html).safe_substitute(report_variables)

    def _send_report(self, user, email=None, message=None, **kwd):
        return self.report

    def send_report(self, user, email=None, message=None, **kwd):
        if self.report is None:
            self.create_report(user, email=email, message=message, **kwd)
        return self._send_report(user, email=email, message=message, **kwd)


class EmailErrorReporter(ErrorReporter):
    def _send_report(self, user, email=None, message=None, **kwd):
        smtp_server = self.app.config.smtp_server
        assert smtp_server, ValueError("Mail is not configured for this Galaxy instance")
        to = self.app.config.error_email_to
        assert to, ValueError("Error reporting has been disabled for this Galaxy instance")

        error_msg = validate_email_str(email)
        if not error_msg and self._can_access_dataset(user):
            to += f", {email.strip()}"
        subject = f"Galaxy tool error report from {email}"
        try:
            subject = f"{subject} ({self.app.toolbox.get_tool(self.job.tool_id, self.job.tool_version).old_id})"
        except Exception:
            pass

        reply_to = user.email if user else None
        return util.send_mail(
            self.app.config.email_from,
            to,
            subject,
            self.report,
            self.app.config,
            html=self.html_report,
            reply_to=reply_to,
        )
