import sys
import tempfile
import pytest

import responses
import requests
from responses import matchers
from requests.exceptions import RequestException

from pentesttools import cli, api, context
from pentesttools.api import API_URL


API_KEY = "random1vjMsyOuDDYUzeJMQARVJdMcHt"


def mock_start_scan():
    header_match = [
        matchers.header_matcher(
            {
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": f"application/json",
            }
        )
    ]
    start_match = [
        matchers.json_params_matcher(
            {
                "tool_id": 170,
                "target_name": "http://target.com/1/2?test=me#123",
                "tool_params": {"scan_type": "light"},
            }
        )
    ]

    responses.post(
        url=API_URL + "scans/",
        json={"data": {"created_id": 490, "target_id": 456}},
        status=201,
        match=header_match + start_match,
    )


def mock_scan_status_error():
    header_match = [
        matchers.header_matcher(
            {
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": f"application/json",
            }
        )
    ]
    status_response = {"status": 404, "message": "The scan with given ID does not exist."}
    responses.get(url=API_URL + "scans/0", json=status_response, status=404, match=header_match)


def mock_scan_status_running():
    header_match = [
        matchers.header_matcher(
            {
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": f"application/json",
            }
        )
    ]
    status_response = {
        "data": {
            "id": 490,
            "tool_id": 170,
            "target_id": 456,
            "workspace_id": None,
            "status_name": "running",
            "vpn_scan": False,
            "progress": 32,
            "result_summary": {
                "text": "Not found",
                "high": None,
                "medium": None,
                "low": None,
                "info": None,
            },
            "start_time": "2021-02-27 17:03:02",
            "end_time": None,
            "duration": 1900,
            "num_tests": 4,
            "num_finished_tests": 2,
            "scan_original_url": False,
            "tool_params": {
                "scan_type": "light",
                "authentication": None,
                "attack_active": ["xss", "sqli", "ssrf", "http_request_smuggling", "log4j_rce"],
                "attack_passive": ["security_headers", "directory_listing", "sensitive_data"],
                "discovery": ["check_robots", "untrusted_certificates", "interesting_files"],
                "spider": {
                    "approach": "spa",
                    "depth": 10,
                    "exclude_urls": [],
                    "include_urls": [],
                },
                "requests_per_second": 10000,
            },
        }
    }

    for _ in range(10):
        responses.get(url=API_URL + "scans/490", json=status_response, status=200, match=header_match)


def mock_scan_status_finished():
    header_match = [
        matchers.header_matcher(
            {
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": f"application/json",
            }
        )
    ]
    finished_status_response = {
        "data": {
            "id": 490,
            "tool_id": 170,
            "target_id": 456,
            "workspace_id": None,
            "status_name": "finished",
            "vpn_scan": False,
            "progress": 32,
            "result_summary": {
                "text": "Not found",
                "high": 1,
                "medium": 1,
                "low": 1,
                "info": 2,
            },
            "start_time": "2021-02-27 17:03:02",
            "end_time": "2021-02-27 18:03:02",
            "duration": 1900,
            "num_tests": 20,
            "num_finished_tests": 20,
            "scan_original_url": False,
            "tool_params": {
                "scan_type": "light",
                "authentication": None,
                "attack_active": ["xss", "sqli", "ssrf", "http_request_smuggling", "log4j_rce"],
                "attack_passive": ["security_headers", "directory_listing", "sensitive_data"],
                "discovery": ["check_robots", "untrusted_certificates", "interesting_files"],
                "spider": {
                    "approach": "spa",
                    "depth": 10,
                    "exclude_urls": [],
                    "include_urls": [],
                },
                "requests_per_second": 10000,
            },
        }
    }
    responses.get(url=API_URL + "scans/490", json=finished_status_response, status=200, match=header_match)


def mock_scan_output():
    header_match = [
        matchers.header_matcher(
            {
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": f"application/json",
            }
        )
    ]
    output_response = {
        "data": {
            "output_type": "finding_list",
            "output_data": {
                "findings": [
                    {
                        "confirmed": True,
                        "cve": ["N/A"],
                        "cvss": None,
                        "id": 857,
                        "name": "Missing security header.",
                        "recommendation": "The Referrer-Policy header shouldbe",
                        "risk_description": "controls how much referrer",
                        "risk_level": 1,
                        "status": "open",
                        "test_description": "Checking Referrer",
                        "test_finished": True,
                        "verified": True,
                        "vuln_description": "Missing security header: Referrer-Policy",
                        "vuln_evidence": {
                            "data": {
                                "headers": ["URL", "Evidence"],
                                "rows": [["http://target.com/", "Response headers do not"]],
                            },
                            "type": "table",
                        },
                        "vuln_id": "WEBSCAN-00-0000029",
                    },
                    {
                        "confirmed": True,
                        "cve": [],
                        "cvss": None,
                        "id": 858,
                        "name": "Communication",
                        "recommendation": "We rcommend",
                        "risk_description": "The communication between",
                        "risk_level": 2,
                        "status": "open",
                        "test_description": "Checking for secure",
                        "test_finished": True,
                        "verified": True,
                        "vuln_description": "Communication is",
                        "vuln_evidence": {
                            "data": {
                                "headers": ["URL", "Evidence"],
                                "rows": [["http://target.ro", "Unencrypted HTTP."]],
                            },
                            "type": "table",
                        },
                        "vuln_id": "WEBSCAN-00-0000005",
                    },
                    {
                        "confirmed": False,
                        "cve": [],
                        "cvss": None,
                        "id": 864,
                        "name": "Vulnerabilities found for server-side software",
                        "recommendation": "We recommend you to upgrade the.",
                        "risk_description": "These expose.",
                        "risk_level": 3,
                        "status": "open",
                        "test_description": "Checking for vulnerabilities ...",
                        "test_finished": True,
                        "verified": False,
                        "vuln_description": "Vulnerabilities found for server-side ",
                        "vuln_evidence": {
                            "data": {
                                "headers": ["Risk Level", "CVSS", "CVE", "Summary", "Affected software"],
                                "rows": [
                                    [
                                        "",
                                        "9.8",
                                        "CVE-2023-25690",
                                        "Some mod_proxy &quot;^/here/(.*)&quot; ",
                                        "http_server 2.4.10",
                                    ],
                                    [
                                        "",
                                        "9",
                                        "CVE-2022-36760",
                                        "Inconsistent Interpretation &apos;",
                                        "http_server 2.4.10",
                                    ],
                                    [
                                        "",
                                        "7.5",
                                        "CVE-2017-3167",
                                        "In Apache httpd 2.2.x before",
                                        "http_server 2.4.10",
                                    ],
                                ],
                            },
                            "type": "table",
                        },
                    },
                    {
                        "confirmed": False,
                        "cve": ["N/A"],
                        "cvss": None,
                        "id": 865,
                        "name": "Nothing was found for",
                        "recommendation": "",
                        "risk_description": "",
                        "risk_level": 0,
                        "status": "open",
                        "test_description": "Checking for client...",
                        "test_finished": True,
                        "verified": False,
                        "vuln_description": "Nothing was found.",
                        "vuln_evidence": {"data": "", "type": "text"},
                        "vuln_id": "NONVULN-00-0000000",
                    },
                    {
                        "confirmed": True,
                        "cve": [],
                        "cvss": None,
                        "id": 867,
                        "name": "Security.txt file",
                        "recommendation": "We recommend allow",
                        "risk_description": "We have detected",
                        "risk_level": 0,
                        "status": "open",
                        "test_description": "Checking for client...",
                        "test_finished": True,
                        "verified": False,
                        "vuln_description": "Missing security.txt",
                        "vuln_evidence": {"data": {"headers": ["URL"], "rows": [["Missing"]]}, "type": "table"},
                        "vuln_id": "WEBSCAN-00-0000059",
                    },
                ]
            },
        }
    }
    responses.get(url=API_URL + "scans/490/output", json=output_response, status=200, match=header_match)


def mock_stop_scan():
    header_match = [
        matchers.header_matcher(
            {
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": f"application/json",
            }
        )
    ]
    output_response = {}
    responses.post(url=API_URL + "scans/490/stop", json=output_response, status=200, match=header_match)


def mock_get_target_by_id():
    header_match = [
        matchers.header_matcher(
            {
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": f"application/json",
            }
        )
    ]
    output_response = {"data": {"name": "http://other.target.com"}}
    responses.get(url=API_URL + "targets/456", json=output_response, status=200, match=header_match)


@responses.activate
def test_cli():
    mock_start_scan()
    mock_scan_status_error()
    mock_scan_status_running()
    mock_scan_status_finished()
    mock_scan_output()
    mock_stop_scan()
    mock_get_target_by_id()

    url = "target.com/1/2?test=me#123"
    api.API_KEY = API_KEY
    api.init()
    sys.argv = ["*.py", url, "--nocolor", "--verbose"]
    ret = cli.cli(context.Context(), {"sec_per_tick": 0.01, "scan_status_wait": 0, "scan_output_wait": 0})
    assert ret == 0

    with tempfile.NamedTemporaryFile() as file:
        sys.argv = ["*.py", url, "--nocolor", "--verbose", "--quiet", "--format", "json", "-o", file.name]
        ret = cli.cli(context.Context(), {"sec_per_tick": 0.01, "scan_status_wait": 0, "scan_output_wait": 0})
        assert ret == 0


@responses.activate
def test_cli_fail_status():
    mock_start_scan()
    mock_scan_status_error()

    url = "target.com/1/2?test=me#123"
    api.API_KEY = API_KEY
    api.init()
    sys.argv = ["*.py", url, "--nocolor", "--verbose"]
    with pytest.raises(RequestException):
        cli.cli(context.Context(), {"sec_per_tick": 0.01, "scan_status_wait": 0, "scan_output_wait": 0})
