from dataclasses import dataclass, field
from typing import List, Dict, Optional, Any

from dataclasses_json import dataclass_json, Undefined

from helpers.md_and_html_convertor import MdAndHTMLConvertor
from pwndoc_custom_fields import PwndocCustomFields
from loguru import logger

HTML_ATTRIBUTES = ['description', 'observation', 'remediation', 'poc']


@dataclass_json(undefined=Undefined.EXCLUDE)  # todo: maybe reconsider? we're throwing away duplicated stuff
@dataclass
class TemplatePwndoc:
    locale: str = "og"

    title: str = ""
    description: str = ''
    observation: str = ''
    remediation: str = ''
    references: List[str] = field(default_factory=list)

    hosts: List[str] = field(default_factory=list)  # only in finding, not in template
    poc: str = ''

    _customFields = PwndocCustomFields()
    _is_template: bool = False

    def set_is_template(self, val: bool):
        self._is_template = val

    def to_pwndoc_html(self, called_from_deprecated_location: bool = False):
        for html_key in HTML_ATTRIBUTES:
            og_val = getattr(self, html_key)
            if og_val is None:
                continue

            new_val = MdAndHTMLConvertor.custom_md_to_pwndoc_html(og_val)  # todo: maybe just use html -> pwndoc html
            changed = og_val != new_val
            setattr(self, html_key, new_val)

            if called_from_deprecated_location and changed:
                logger.trace(f'HTML conversion for field "{html_key}" happened inside TemplatePwndoc.to_pwndoc_dict()')

    def to_pwndoc_dict(self) -> Dict[str, Any]:
        self.to_pwndoc_html()  # todo: in future set called_from_deprecated_location=True
        # Some attributes may be HTML and converted later.
        answer = {
            "title": self.title,
            "description": self.description,
            "remediation": self.remediation,
            "references": self.references,
            "observation": self.observation,
            "locale": self.locale,

            # These are not in 'Templates' but only in 'Findings'
            "poc": self.poc,
            "scope": "<br>".join(self.hosts),
            "category": "VulnCategory1",

            "customFields": self._customFields.get_pwndoc_repr(),
        }

        if self._is_template:
            del answer["poc"]
            del answer["scope"]
            del answer["category"]

        return answer


@dataclass
class MultilocaleTemplateWrapperPwndoc:
    status: int = 0
    _id: Optional[str] = None
    cvssv3: Optional[str] = None
    category: str = "VulnCategory1"
    details: List[TemplatePwndoc] = field(default_factory=list)

    def get_fid(self) -> Optional[str]:
        return self.details[0].fid if len(self.details) else None

    def get_pwndoc_repr(self):
        answer = {
            'status': self.status,
            '_id': self._id,
            'cvssv3': self.cvssv3,
            'category': self.category,
            'details': [x.to_pwndoc_dict() for x in self.details]
        }
        return answer


@dataclass
class TNSTemplatePwndoc(TemplatePwndoc):
    fid: str = ""

    # severity_color: str = "#4a86e8"  # blue
    severity_label: str = ""
    attack_complexicity_label: str = ""
    system_impact_label: str = ""
    author: str = ""
    ignore_pluginoutput_checkbox: List[str] = field(default_factory=list)

    def add_pwndoc_custom_fields(self):
        custom_fields = self._customFields

        custom_fields.set_field("fid", self.fid)

        custom_fields.set_field("severity_label", self.severity_label)
        custom_fields.set_field("attack_complexicity_label", self.attack_complexicity_label)
        custom_fields.set_field("system_impact_label", self.system_impact_label)
        custom_fields.set_field("ignore_pluginoutput_checkbox", self.ignore_pluginoutput_checkbox)  # todo: fix typing

        if self._is_template:
            custom_fields.set_field("author", self.author)

    def set_custom_field(self, field_name: str, field_value: str):
        self._customFields.set_field(field_name, field_value)

    def to_pwndoc_dict(self) -> Dict[str, Any]:
        self.add_pwndoc_custom_fields()
        return super().to_pwndoc_dict()


def get_fid_from_raw_finding(finding: dict) -> Optional[str]:
    fid_pwndoc_custom_field_id = PwndocCustomFields.get_custom_field_by_label("fid").get("_id", "")

    custom_fields = finding.get("customFields", [])
    for single_field in custom_fields:
        try:
            field_id = single_field.get("customField", {}).get("_id", "")
        except AttributeError:
            field_id = single_field.get("customField", "")
        if field_id == fid_pwndoc_custom_field_id:
            return single_field.get("text")
    return None
