# -*- coding: utf-8 -*-
import platform
import sys
import json
import os
import textwrap

# python 2.7 compat
try:
    FileNotFoundError
except NameError:
    FileNotFoundError = IOError

try:
    system = platform.system()
    python_version = ".".join([str(i) for i in sys.version_info[0:2]])
    # get_terminal_size exists on Python 3.4 but isn't working on windows
    if system == "Windows" and python_version in ["3.4"]:
        raise ImportError
    from shutil import get_terminal_size
except ImportError:
    # fallback for python < 3
    import subprocess
    from collections import namedtuple

    def get_terminal_size():
        size = namedtuple("_", ["rows", "columns"])
        try:
            rows, columns = subprocess.check_output(
                ['stty', 'size'],
                stderr=subprocess.STDOUT
            ).split()
            return size(rows=int(rows), columns=int(columns))
        # this won't work
        # - on windows (FileNotFoundError/OSError)
        # - python 2.6 (AttributeError)
        # - if the output is somehow mangled (ValueError)
        except (ValueError, FileNotFoundError, OSError,
                AttributeError, subprocess.CalledProcessError):
            return size(rows=0, columns=0)


def get_advisory(vuln):
    return vuln.advisory if vuln.advisory else "No advisory found for this vulnerability."


class SheetReport(object):
    REPORT_BANNER = r"""
+==============================================================================+
|                                                                              |
|                               /$$$$$$            /$$                         |
|                              /$$__  $$          | $$                         |
|           /$$$$$$$  /$$$$$$ | $$  \__//$$$$$$  /$$$$$$   /$$   /$$           |
|          /$$_____/ |____  $$| $$$$   /$$__  $$|_  $$_/  | $$  | $$           |
|         |  $$$$$$   /$$$$$$$| $$_/  | $$$$$$$$  | $$    | $$  | $$           |
|          \____  $$ /$$__  $$| $$    | $$_____/  | $$ /$$| $$  | $$           |
|          /$$$$$$$/|  $$$$$$$| $$    |  $$$$$$$  |  $$$$/|  $$$$$$$           |
|         |_______/  \_______/|__/     \_______/   \___/   \____  $$           |
|                                                          /$$  | $$           |
|                                                         |  $$$$$$/           |
|  by pyup.io                                              \______/            |
|                                                                              |
+==============================================================================+
    """.strip()

    TABLE_HEADING = r"""
+============================+===========+==========================+==========+
| package                    | installed | affected                 | ID       |
+============================+===========+==========================+==========+
    """.strip()

    REPORT_HEADING = r"""
| REPORT                                                                       |
    """.strip()

    REPORT_SECTION = r"""
+==============================================================================+
    """.strip()

    REPORT_FOOTER = r"""
+==============================================================================+
    """.strip()

    @staticmethod
    def render(vulns, full, checked_packages, used_db):
        db_format_str = '{: <' + str(51 - len(str(checked_packages))) + '}'
        status = "| checked {packages} packages, using {db} |".format(
            packages=checked_packages,
            db=db_format_str.format(used_db),
            section=SheetReport.REPORT_SECTION
        )
        if vulns:
            table = []
            for n, vuln in enumerate(vulns):
                table.append("| {:26} | {:9} | {:24} | {:8} |".format(
                    vuln.name[:26],
                    vuln.version[:9],
                    vuln.spec[:24],
                    vuln.vuln_id
                ))
                if full:
                    table.append(SheetReport.REPORT_SECTION)

                    descr = get_advisory(vuln)

                    for pn, paragraph in enumerate(descr.replace('\r', '').split('\n\n')):
                        if pn:
                            table.append("| {:76} |".format(''))
                        for line in textwrap.wrap(paragraph, width=76):
                            try:
                                table.append("| {:76} |".format(line.encode('utf-8')))
                            except TypeError:
                                table.append("| {:76} |".format(line))
                    # append the REPORT_SECTION only if this isn't the last entry
                    if n + 1 < len(vulns):
                        table.append(SheetReport.REPORT_SECTION)
            return "\n".join(
                [SheetReport.REPORT_BANNER, SheetReport.REPORT_HEADING, status, SheetReport.TABLE_HEADING,
                 "\n".join(table), SheetReport.REPORT_FOOTER]
            )
        else:
            content = "| {:76} |".format("No known security vulnerabilities found.")
            return "\n".join(
                    [SheetReport.REPORT_BANNER, SheetReport.REPORT_HEADING, status, SheetReport.REPORT_SECTION,
                     content, SheetReport.REPORT_FOOTER]
                )


class BasicReport(object):
    """Basic report, intented to be used for terminals with < 80 columns"""

    @staticmethod
    def render(vulns, full, checked_packages, used_db):
        table = [
            "safety report",
            "checked {packages} packages, using {db}".format(
                packages=checked_packages,
                db=used_db
            ),
            "---"
        ]
        if vulns:

            for vuln in vulns:
                table.append("-> {}, installed {}, affected {}, id {}".format(
                    vuln.name,
                    vuln.version[:13],
                    vuln.spec[:27],
                    vuln.vuln_id
                ))
                if full:
                    table.append(get_advisory(vuln))
                    table.append("--")
        else:
            table.append("No known security vulnerabilities found.")
        return "\n".join(
            table
        )


class JsonReport(object):
    """Json report, for when the output is input for something else"""

    @staticmethod
    def render(vulns, full):
        return json.dumps(vulns, indent=4, sort_keys=True)


class BareReport(object):
    """Bare report, for command line tools"""
    @staticmethod
    def render(vulns, full):
        return " ".join(set([v.name for v in vulns]))


def get_used_db(key, db):
    key = key if key else os.environ.get("SAFETY_API_KEY", False)
    if key:
        return "pyup.io's DB"
    if db == '':
        return 'default DB'
    return "local DB"


def report(vulns, full=False, json_report=False, bare_report=False, checked_packages=0, db=None, key=None):
    if bare_report:
        return BareReport.render(vulns, full=full)
    if json_report:
        return JsonReport.render(vulns, full=full)
    size = get_terminal_size()
    used_db = get_used_db(key=key, db=db)
    if size.columns >= 80:
        return SheetReport.render(vulns, full=full, checked_packages=checked_packages, used_db=used_db)
    return BasicReport.render(vulns, full=full, checked_packages=checked_packages, used_db=used_db)
