import time
from dataclasses import dataclass, field
from typing import List

from secator.definitions import (CONFIDENCE, CVSS_SCORE, EXTRA_DATA, ID,
								 MATCHED_AT, NAME, REFERENCE, SEVERITY, TAGS)
from secator.output_types import OutputType
from secator.utils import rich_to_ansi, rich_escape as _s


@dataclass
class Vulnerability(OutputType):
	name: str
	provider: str = ''
	id: str = ''
	matched_at: str = ''
	ip: str = field(default='', compare=False)
	confidence: str = 'low'
	severity: str = 'unknown'
	cvss_score: float = 0
	tags: List[str] = field(default_factory=list)
	extra_data: dict = field(default_factory=dict, compare=False)
	description: str = field(default='', compare=False)
	references: List[str] = field(default_factory=list, compare=False)
	reference: str = field(default='', compare=False)
	confidence_nb: int = 0
	severity_nb: int = 0
	_source: str = field(default='', repr=True)
	_type: str = field(default='vulnerability', repr=True)
	_timestamp: int = field(default_factory=lambda: time.time(), compare=False)
	_uuid: str = field(default='', repr=True, compare=False)
	_context: dict = field(default_factory=dict, repr=True, compare=False)
	_tagged: bool = field(default=False, repr=True, compare=False)
	_duplicate: bool = field(default=False, repr=True, compare=False)
	_related: list = field(default_factory=list, compare=False)

	_table_fields = [
		MATCHED_AT,
		SEVERITY,
		CONFIDENCE,
		NAME,
		ID,
		CVSS_SCORE,
		TAGS,
		EXTRA_DATA,
		REFERENCE
	]
	_sort_by = ('confidence_nb', 'severity_nb', 'matched_at', 'cvss_score')

	def __post_init__(self):
		super().__post_init__()
		severity_map = {
			'critical': 0,
			'high': 1,
			'medium': 2,
			'low': 3,
			'info': 4,
			'unknown': 5,
			None: 6
		}
		self.severity = self.severity.lower()  # normalize severity
		self.severity_nb = severity_map.get(self.severity, 6)
		self.confidence_nb = severity_map[self.confidence]
		if len(self.references) > 0:
			self.reference = self.references[0]

	def __repr__(self):
		data = self.extra_data
		if 'data' in data and isinstance(data['data'], list):
			data = ','.join(data['data'])
		elif isinstance(data, dict):
			data = ', '.join([f'{k}:{v}' for k, v in data.items()])
		data = _s(data)
		tags = self.tags
		colors = {
			'critical': 'bold red',
			'high': 'red',
			'medium': 'yellow',
			'low': 'green',
			'info': 'magenta',
			'unknown': 'dim magenta'
		}
		c = colors.get(self.severity, 'dim magenta')
		s = rf'🚨 \[[green]{_s(self.name)} [link={_s(self.reference)}]🡕[/link][/]] \[[{c}]{self.severity}[/]] {_s(self.matched_at)}'  # noqa: E501
		if tags:
			tags_str = ','.join(tags)
			s += rf' \[[cyan]{_s(tags_str)}[/]]'
		if data:
			s += rf' \[[yellow]{str(data)}[/]]'
		if self.confidence == 'low':
			s = f'[dim]{s}[/]'
		return rich_to_ansi(s)

	def __str__(self):
		return self.matched_at + ' -> ' + self.name
