import io
import json
from pathlib import Path
import os.path
from typing import List, Optional, Callable, Any, Tuple, Set
import pytest
from pytest_steps import test_steps
from copy import deepcopy

from api_process_findings import ProcessingSettings
from tests.conftest import TMP_PATH, PWNDOC_DANGER_OVERRIDE
from tests.test_findings_parsing import test_connectivity, PwnDocCommunication
from tests.test_templating import delete_templates
from tests.helper_generate_finding_file import generate_text_for_issues
import config
from loguru import logger

# Nearly everything has a burp_ prefix, because I'm modifying an burp scan to create findings.

RESERVED_FIDS = {
    'ignored': ['burp_importer_test_ignored_fid1', 'burp_importer_test_ignored_fid2'],
    "IMPORTER_TEST_ALIAS1": ["burp_importer_test_alias_fid1", "burp_importer_test_alias_fid2"],
    "IMPORTER_TEST_ALIAS2": ["burp_importer_test_alias_fid3", "burp_importer_test_alias_fid4"],
    "IMPORTER_TEST_GROUP_1": ["IMPORTER_TEST_ALIAS2", "burp_importer_test_ignored_fid2", "burp_importer_test_fid1", "burp_importer_test_fid2"],
}


class FindingHelper:
    @staticmethod
    def partial_fids_to_full_fids(partial_fids: Set[str]) -> Set[str]:
        return set(f"burp_{partial_fid}" for partial_fid in partial_fids)

    @staticmethod
    def full_fids_to_partial_fids(full_ids: Set[str]) -> Set[str]:
        new_ids = set()
        for single_id in full_ids:
            new_ids.add(single_id.replace("burp_", "", 1))
        return new_ids

    @staticmethod
    def run_evaluation(client, test_name: str, partial_fids: Set[str], expected_fids: Set[str], profile: str = 'importer_test'):
        delete_templates(client)
        logger.debug(f"{test_name=} | {partial_fids=} | {expected_fids=}")

        finding_text = generate_text_for_issues(list(partial_fids))

        output_filename = f"{test_name}.json"
        folder_name = PwnDocCommunication.process_findings(
            client,
            output_name=output_filename,
            files=[(io.BytesIO(finding_text.encode()), output_filename)],
            profile=profile,
        )

        ps: ProcessingSettings = ProcessingSettings.load_from_folder_name(folder_name)
        with open(ps.get_result_path(), encoding="utf8") as f:
            data = json.load(f)

        imported_fids = set([x["fid"] for x in data])
        assert imported_fids == expected_fids


@pytest.mark.skipif(not PWNDOC_DANGER_OVERRIDE, reason="PWNDOC_DANGER_OVERRIDE must be allowed to enable testing of interaction with PwnDoc")
@pytest.mark.dependency(depends=["test_connectivity"])
class TestGroupingAndAliasing:

    # todo: even grouped and aliased templates seem to be upload to PwnDoc? Not as findings, just as templates, but still?

    @pytest.mark.parametrize("test_name, input_fids, expected_fids", [
        ('regular_import', {'burp_test1', 'burp_test2'}, {'burp_test1', 'burp_test2'}),
        ('empty_import', set(), set()),
        ('ignored_findings', set(RESERVED_FIDS['ignored']), set()),
        ('import_alias', {RESERVED_FIDS['IMPORTER_TEST_ALIAS1'][0]}, {'IMPORTER_TEST_ALIAS1'}),
        ('no_ignoring_of_aliased', {RESERVED_FIDS['IMPORTER_TEST_ALIAS2'][0]}, {'IMPORTER_TEST_ALIAS2'}),  # Ignoring seems to happen before aliasing. Aliased finding is not ignored.
        ('grouping_of_findings', set(RESERVED_FIDS['IMPORTER_TEST_GROUP_1'][1:]), {'IMPORTER_TEST_GROUP_1'}),
    ])
    def test_all(self, client, test_name: str, input_fids: Set[str], expected_fids: Set[str]):
        input_partial_fids = FindingHelper.full_fids_to_partial_fids(input_fids)
        FindingHelper.run_evaluation(client, test_name, input_partial_fids, expected_fids)

