import weasyprint
import os
import json
import hashlib
import re
import sys

from weasyprint import HTML
from pathlib import Path
from datetime import datetime
from utils import get_config


class Report(object):

    def __init__(self, capture_directory, analysis_duration):
        self.capture_directory = capture_directory
        self.alerts = self.read_json(os.path.join(capture_directory, "assets/alerts.json"))
        self.records = self.read_json(os.path.join(capture_directory, "assets/records.json"))
        self.methods = self.read_json(os.path.join(capture_directory, "assets/detection_methods.json"))
        self.device = self.read_json(os.path.join(capture_directory, "assets/device.json"))
        self.capinfos = self.read_json(os.path.join(capture_directory, "assets/capinfos.json"))
        self.instance = self.read_json(os.path.join(capture_directory, "assets/instance.json"))
        self.analysis_duration = analysis_duration

        with open(os.path.join(self.capture_directory, "capture.pcap"), "rb") as f:
            self.capture_sha1 = hashlib.sha1(f.read()).hexdigest()

        self.userlang = get_config(("frontend", "user_lang"))

        # Load template language
        if not re.match("^[a-z]{2,3}$", self.userlang):
            self.userlang = "en"
        with open(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "locales/{}.json".format(self.userlang))) as f:
            self.template = json.load(f)["report"]

    def read_json(self, json_path):
        """Read a JSON 

        Args:
            json_path (_type_): _description_

        Returns:
            _type_: _description_
        """
        with open(json_path, "r") as json_file:
            return json.load(json_file)

    def generate_report(self):
        """Generate the full report and save it as report.pdf """

        content = self.generate_page_header()
        content += self.generate_header()
        content += self.generate_warning()
        content += self.generate_alerts()
        content += self.generate_suspect_conns_block()
        content += self.generate_uncat_conns_block()
        content += self.generate_whitelist_block()

        htmldoc = HTML(string=content, base_url="").write_pdf()
        Path(os.path.join(self.capture_directory,
                          "report.pdf")).write_bytes(htmldoc)

    def generate_warning(self):
        """Generate the main warning message on the report

        Returns:
            string: HTML code of the main warning message.
        """
        if len(self.alerts["high"]):
            msg = "<div class=\"warning high\">"
            msg += self.template["high_msg"].format(
                self.nb_translate(len(self.alerts["high"])))
            msg += "</div>"
            return msg
        elif len(self.alerts["moderate"]):
            msg = "<div class=\"warning moderate\">"
            msg += self.template["moderate_msg"].format(
                self.nb_translate(len(self.alerts["moderate"])))
            msg += "</div>"
            return msg
        elif len(self.alerts["low"]):
            msg = "<div class=\"warning low\">"
            msg += self.template["low_msg"].format(
                self.nb_translate(len(self.alerts["low"])))
            msg += "</div>"
            return msg
        else:
            msg = "<div class=\"warning low\">"
            msg += self.template["none_msg"]
            msg += "</div>"
            return msg

    def nb_translate(self, nb):
        """ Translate a number in a string.

        Args:
            nb (int): integer

        Returns:
            string: A translated string related to the integer
        """
        a = self.template["numbers"]
        return a[nb-1] if nb <= 9 else str(nb)

    def generate_suspect_conns_block(self):
        """Generate the block and the table of communications categorized as suspect.

        Returns:
            str: HTML block of the suspect communications
        """

        tbody = ""

        for record in self.records:
            if record["suspicious"] == True:
                tbody += "<tr>"
                tbody += f"<td>{', '.join([p['name'] for p in record['protocols']])}</td>"
                tbody += f"<td>{', '.join(record['domains'])}</td>"
                tbody += f"<td>{record['ip_dst']}</td>"
                tbody += f"<td>{', '.join([str(p['port']) if p['port'] != -1 else '--' for p in record['protocols']])}</td>"
                tbody += "</tr>"

        if len(tbody):
            title = f"<h2>{self.template['suspect_title']}</h2>"
            table = "<table>"
            table += "    <thead>"
            table += "        <tr>"
            table += f"             <th>{self.template['protocol']}</th>"
            table += f"             <th>{self.template['domain']}</th>"
            table += f"             <th>{self.template['dst_ip']}</th>"
            table += f"             <th>{self.template['dst_port']}</th>"
            table += "        </tr>"
            table += "    </thead>"
            table += "<tbody>"
            table += tbody
            table += "</tbody></table>"
            return title + table
        else:
            return ""

    def generate_uncat_conns_block(self):
        """Generate the block and the table of the uncategorized communications

        Returns:
            str: HTML block of the uncategorized communications
        """
        tbody = ""

        for record in self.records:
            if record["suspicious"] == False and record["whitelisted"] == False:
                tbody += "<tr>"
                tbody += f"<td>{', '.join([p['name'] for p in record['protocols']])}</td>"
                tbody += f"<td>{', '.join(record['domains'])}</td>"
                tbody += f"<td>{record['ip_dst']}</td>"
                tbody += f"<td>{', '.join([str(p['port']) if p['port'] != -1 else '--' for p in record['protocols']])}</td>"
                tbody += "</tr>"

        if len(tbody):
            title = "<h2>{}</h2>".format(self.template["uncat_title"])
            table = "<table>"
            table += "    <thead>"
            table += "        <tr>"
            table += f"             <th>{self.template['protocol']}</th>"
            table += f"             <th>{self.template['domain']}</th>"
            table += f"             <th>{self.template['dst_ip']}</th>"
            table += f"             <th>{self.template['dst_port']}</th>"
            table += "        </tr>"
            table += "    </thead>"
            table += "<tbody>"
            table += tbody
            table += "</tbody></table>"
            return title + table
        else:
            return ""

    def generate_whitelist_block(self):
        """Generate the block and the table of the whitelisted communications

        Returns:
            str: HTML block of the whitelisted communications
        """

        tbody = ""

        for record in self.records:
            if record["whitelisted"] == True:
                tbody += "<tr>"
                tbody += f"<td>{', '.join([p['name'] for p in record['protocols']])}</td>"
                tbody += f"<td>{', '.join(record['domains'])}</td>"
                tbody += f"<td>{record['ip_dst']}</td>"
                tbody += f"<td>{', '.join([str(p['port']) if p['port'] != -1 else '--' for p in record['protocols']])}</td>"
                tbody += "</tr>"

        if len(tbody):
            title = "<h2>{}</h2>".format(self.template["whitelist_title"])
            table = "<table>"
            table += "    <thead>"
            table += "        <tr>"
            table += f"             <th>{self.template['protocol']}</th>"
            table += f"             <th>{self.template['domain']}</th>"
            table += f"             <th>{self.template['dst_ip']}</th>"
            table += f"             <th>{self.template['dst_port']}</th>"
            table += "        </tr>"
            table += "    </thead>"
            table += "<tbody>"
            table += tbody
            table += "</tbody></table>"
            return title + table
        else:
            return ""

    def generate_header(self):
        """Generate the report headers with the capture's metadata.

        Returns:
            str: HTML block containing the data.
        """
        header = "<div class=\"header\">"
        header += "<div class=\"logo\"></div>"
        header += f"<p><br /><strong>{self.template['device_mac']}: {self.device['mac_address']}</strong><br />"
        header += f"{self.template['detection_methods']}: {'☑' if self.methods['iocs'] else '☐'} IOCs {'☑' if self.methods['heuristics'] else '☐'}  Heuristics {'☑' if self.methods['active'] else '☐'} Active analysis <br />"
        header += f"{self.template['capture_sha1']}: {self.capture_sha1}<br />"
        header += f"{self.template['instance_uuid']}: {self.instance['instance_uuid']}<br />"
        header += f"{self.template['report_generated_on']} {datetime.now().strftime('%d/%m/%Y - %H:%M:%S')}<br />"
        if self.capinfos is not None:
            header += f"{self.template['capture_duration']}: {self.capinfos['Capture duration'].split(' ')[0]} {self.template['seconds']}<br />"
            header += f"{self.template['analysis_duration']}: {self.analysis_duration} {self.template['seconds']}<br />"
            header += f"{self.template['packets_number']}: {self.capinfos['Number of packets']}<br />"
        header += "</p>"
        header += "</div>"
        return header

    def generate_alerts(self):
        """Generate a block embedding the alerts triggered during the analysis.

        Returns:
            str: HTML block containing the data.
        """
        alerts = "<ul class=\"alerts\">"
        for alert in self.alerts["high"]:
            alerts += "<li class =\"alert\">"
            alerts += "<span class=\"high-label\">High</span>"
            alerts += "<span class=\"alert-id\">{}</span>".format(alert["id"])
            alerts += "<div class = \"alert-body\">"
            alerts += "<span class=\"title\">{}</span>".format(alert["title"])
            alerts += "<p class=\"description\">{}</p>".format(
                alert["description"])
            alerts += "</div>"
            alerts += "</li>"

        for alert in self.alerts["moderate"]:
            alerts += "<li class =\"alert\">"
            alerts += "<span class=\"moderate-label\">moderate</span>"
            alerts += "<span class=\"alert-id\">{}</span>".format(alert["id"])
            alerts += "<div class = \"alert-body\">"
            alerts += "<span class=\"title\">{}</span>".format(alert["title"])
            alerts += "<p class=\"description\">{}</p>".format(
                alert["description"])
            alerts += "</div>"
            alerts += "</li>"
        for alert in self.alerts["low"]:
            alerts += "<li class =\"alert\">"
            alerts += "<span class=\"low-label\">low</span>"
            alerts += "<span class=\"alert-id\">{}</span>".format(alert["id"])
            alerts += "<div class = \"alert-body\">"
            alerts += "<span class=\"title\">{}</span>".format(alert["title"])
            alerts += "<p class=\"description\">{}</p>".format(
                alert["description"])
            alerts += "</div>"
            alerts += "</li>"

        alerts += "</ul>"
        return alerts

    def generate_page_footer(self):
        """Generate the page footer

        Returns:
            str: HTML block closing the page
        """
        return "</body></html>"

    def generate_page_header(self):
        """Generate the page header

        Returns:
            str: HTML block containing the page header with the CSS.
        """
        return """<html
                    <head>
                        <style>
                            * {
                                font-family: Arial, Helvetica, sans-serif;
                            }

                            h2 {
                                padding-top: 30px;
                                font-weight: 400;
                                font-size: 18px;
                            }

                            td {
                                width: auto;
                                padding: 10px;
                            }

                            table {
                                background: #FFF;
                                border: 2px solid #FAFAFA;
                                border-radius: 5px;
                                border-collapse: separate;
                                border-spacing: 0px;
                                width: 100%;
                                font-size: 12px;
                            }

                            p {
                                font-size: 13px;
                            }

                            thead tr th {
                                border-bottom: 1px solid #CCC;
                                border-collapse: separate;
                                border-spacing: 5px 5px;
                                background-color: #FFF;
                                padding: 10px;
                                text-align: left;
                            }

                            tbody tr#first td {
                                border-top: 3px solid #4d4d4d;
                                border-collapse: separate;
                                border-spacing: 5px 5px;
                            }

                            tr:nth-of-type(odd) {
                                background-color: #fafafa;
                            }

                            .logo {
                                background-image: url("data:image/svg+xml;base64;base64,[encoded data, 10084 characters]");
                                width: 230px;
                                height: 60px;
                                background-size: cover;
                                position: absolute;
                                right: 0px;
                            }

                            .warning {
                                padding: 10px;
                                text-align: center;
                                border-radius: 5px;
                                color: #FFF;
                                margin-top: 40px;
                                margin-bottom: 40px;
                                font-weight:900;
                            }

                            .high {
                                background-color: #F44336;
                            }

                            .moderate {
                                background-color: #ff7e33;
                            }

                            .low {
                                background-color: #4fce0e;
                            }

                            ul {
                                list-style: none;
                                margin: 0;
                                padding: 0;
                            }

                            .alert {
                                margin-top: 15px;
                            }

                            .alert-body {
                                background-color: #FFF;
                                list-style: none;
                                padding: 10px;
                                border-radius: 5px;
                                border: 1px solid #EEE;
                                margin-top: 3px;
                            }

                            .alert-body>.title {
                                display: block;
                                padding: 5px 5px 5px 10px;
                                font-size: 13px;
                            }

                            .high-label {
                                background-color: #F44336;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 3px 0px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-left: 10px;
                            }

                            .moderate-label {
                                background-color: #ff7e33;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 3px 0px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-left: 10px;
                            }

                            .low-label {
                                background-color: #4fce0e;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 3px 0px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-left: 10px;
                            }

                            .description {
                                margin: 0;
                                padding: 10px;
                                color:#333;
                                font-size:12px;
                            }

                            ul {
                                list-style: none;
                                margin: 0;
                                padding: 0;
                            }

                            .alert-id {
                                background-color: #636363;
                                padding: 5px;
                                text-transform: uppercase;
                                font-size: 10px;
                                font-weight: bold;
                                border-radius: 0px 3px 0px 0px;
                                margin: 0px;
                                color: #FFF;
                                margin-right: 10px;
                            }
                 
                            .header>p {
                                font-size:12px;
                            }
                            @page {
                                @top-center { 
                                    content: "REPORT_HEADER - Page " counter(page) " / " counter(pages) ".";
                                    font-size:12px;
                                    color:#CCC;
                                }
                                @bottom-center { 
                                    content: "REPORT_FOOTER";
                                    font-size:12px;  
                                    color:#CCC;
                                }
                            }   
                        </style>
                    </head>
                    <body>""".replace("REPORT_HEADER", "{} {}".format(self.template["report_for_the_capture"], self.capture_sha1)).replace("REPORT_FOOTER", self.template["report_footer"])
