import re

from typing import Tuple, Dict
from .common.enums import DetailType
from .patterns import STARTING_DETAILS_MAPPING, DETAILS_PATTERNS_MAPPING
from .patterns.common import REPLACEMENT_PHRASES, NOISE_PATTERNS


class CVEDriller:
    def __init__(self, verbose: bool = False):
        self.verbose = verbose

    def log_info(self, message: str):
        if self.verbose:
            print(message)

    def extract_from_patterns(self, desc: str, patterns: dict):
        for name, pattern in patterns.items():
            match = re.compile(pattern).match(desc)

            if match:
                self.log_info(f"{name} - {pattern}")
                return match.groupdict()

        return None

    @staticmethod
    def normalize_phrases(desc: str) -> str:
        if desc[-1] != '.':
            desc += '.'

        desc = re.sub(r'\s+', ' ', desc)
        desc = re.sub(r"\"", "", desc)

        for old, new in REPLACEMENT_PHRASES.items():
            # TODO: should separate the replacement_phrases into categories and apply only one per each category
            desc = re.sub(old, new, desc)

        return desc

    @staticmethod
    def remove_noise(desc: str) -> str:
        for pattern in NOISE_PATTERNS:
            desc = re.sub(pattern, "", desc)

        return desc

    def __call__(self, desc: str) -> Tuple[DetailType | None, Dict[str, str] | None]:
        desc = desc.strip()
        desc = self.normalize_phrases(desc)
        desc = self.remove_noise(desc)

        last_matching_detail_type = None

        self.log_info(f"Description: {desc}")
        for detail_type, pattern in STARTING_DETAILS_MAPPING.items():
            details_patterns = DETAILS_PATTERNS_MAPPING[detail_type]

            if re.match(pattern, desc):
                last_matching_detail_type = detail_type
                extracted_details = self.extract_from_patterns(desc, details_patterns)
                self.log_info(f"Found starting pattern: {detail_type}")

                if extracted_details:
                    return detail_type, extracted_details

        return last_matching_detail_type, None
