from __future__ import annotations

import logging
from abc import ABCMeta, abstractmethod
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Optional

if TYPE_CHECKING:
    from safety.models import Vulnerability

NOT_IMPLEMENTED = "You should implement this."

LOG = logging.getLogger(__name__)


class FormatterAPI:
    """
    Strategy Abstract class, with all the render methods that the concrete implementations should support.
    """

    __metaclass__ = ABCMeta

    def __init__(self, **kwargs: Any) -> None:
        """
        Dummy initializer for the FormatterAPI class.
        """
        pass

    @abstractmethod
    def render_vulnerabilities(
        self,
        announcements: List[Dict[str, Any]],
        vulnerabilities: list[Vulnerability],
        remediations: Dict[str, Any],
        full: bool,
        packages: List[Dict[str, Any]],
        fixes: Tuple = (),
    ) -> Optional[str]:
        """
        Render the vulnerabilities report.

        Args:
            announcements (List[Dict[str, Any]]): List of announcements.
            vulnerabilities (list[Vulnerability]): List of vulnerabilities.
            remediations (Dict[str, Any]): Dictionary of remediations.
            full (bool): Whether to render a full report.
            packages (List[Dict[str, Any]]): List of packages.
            fixes (Tuple, optional): Tuple of fixes. Defaults to ().

        Returns:
            Optional[str]: Rendered vulnerabilities report.
        """
        raise NotImplementedError(NOT_IMPLEMENTED)  # pragma: no cover

    @abstractmethod
    def render_licenses(
        self, announcements: List[Dict[str, Any]], licenses: List[Dict[str, Any]]
    ) -> Optional[str]:
        """
        Render the licenses report.

        Args:
            announcements (List[Dict[str, Any]]): List of announcements.
            licenses (List[Dict[str, Any]]): List of licenses.

        Returns:
            Optional[str]: Rendered licenses report.
        """
        raise NotImplementedError(NOT_IMPLEMENTED)  # pragma: no cover

    @abstractmethod
    def render_announcements(
        self, announcements: List[Dict[str, Any]]
    ) -> str | list[str] | None:
        """
        Render the announcements.

        Args:
            announcements (List[Dict[str, Any]]): List of announcements.

        Returns:
            str | list[str] | None: Rendered announcements.
        """
        raise NotImplementedError(NOT_IMPLEMENTED)  # pragma: no cover


class SafetyFormatter(FormatterAPI):
    """
    Formatter class that implements the FormatterAPI to render reports in various formats.
    """

    def __init__(self, output: str, **kwargs: Any) -> None:
        """
        Initialize the SafetyFormatter with the specified output format.

        Args:
            output (str): The output format (e.g., 'json', 'html', 'bare', 'text').
            **kwargs: Additional keyword arguments.
        """
        from safety.formatters.screen import ScreenReport
        from safety.formatters.text import TextReport
        from safety.formatters.json import JsonReport
        from safety.formatters.bare import BareReport
        from safety.formatters.html import HTMLReport

        self.format = ScreenReport(**kwargs)

        if output == "json":
            self.format = JsonReport(**kwargs)
        elif output == "html":
            self.format = HTMLReport(**kwargs)
        elif output == "bare":
            self.format = BareReport(**kwargs)
        elif output == "text":
            self.format = TextReport(**kwargs)

    def render_vulnerabilities(
        self,
        announcements: List[Dict[str, Any]],
        vulnerabilities: list[Vulnerability],
        remediations: Dict[str, Any],
        full: bool,
        packages: List[Dict[str, Any]],
        fixes: Tuple = (),
    ) -> Optional[str]:
        """
        Render the vulnerabilities report.

        Args:
            announcements (List[Dict[str, Any]]): List of announcements.
            vulnerabilities (list[Vulnerability]): List of vulnerabilities.
            remediations (Dict[str, Any]): Dictionary of remediations.
            full (bool): Whether to render a full report.
            packages (List[Dict[str, Any]]): List of packages.
            fixes (Tuple, optional): Tuple of fixes. Defaults to ().

        Returns:
            Optional[str]: Rendered vulnerabilities report.
        """
        LOG.info(
            "Safety is going to render_vulnerabilities with format: %s", self.format
        )
        return self.format.render_vulnerabilities(
            announcements, vulnerabilities, remediations, full, packages, fixes
        )

    def render_licenses(
        self, announcements: List[Dict[str, Any]], licenses: List[Dict[str, Any]]
    ) -> Optional[str]:
        """
        Render the licenses report.

        Args:
            announcements (List[Dict[str, Any]]): List of announcements.
            licenses (List[Dict[str, Any]]): List of licenses.

        Returns:
            Optional[str]: Rendered licenses report.
        """
        LOG.info("Safety is going to render_licenses with format: %s", self.format)
        return self.format.render_licenses(announcements, licenses)

    def render_announcements(
        self, announcements: List[Dict[str, Any]]
    ) -> str | list[str] | None:
        """
        Render the announcements.

        Args:
            announcements (List[Dict[str, Any]]): List of announcements.

        Returns:
            str | list[str] | None: Rendered announcements.
        """
        LOG.info("Safety is going to render_announcements with format: %s", self.format)
        return self.format.render_announcements(announcements)
