from collections import OrderedDict
from itertools import chain

from parallels.core.reports.model.issue import Issue
from parallels.core.reports.report_writer import ReportWriter
from parallels.core.utils.entity import Entity


class Report(ReportWriter, Entity):
    def __init__(self, type, name, issues=None, children=None):
        self.type = type
        self.name = name
        self.issues = issues if issues is not None else []

        self.children = []

        # internal structure for fast access to child reports and keeping them unique by type and name
        self._children_dict = OrderedDict()

        if children is not None:
            for child in children:
                self.children.append(child)
                self._children_dict[(child.type, child.name)] = child

    def subtarget(self, type, name):
        if (type, name) not in self._children_dict:
            self._children_dict[(type, name)] = Report(type, name)
            self.children.append(self._children_dict[(type, name)])
        return self._children_dict[(type, name)]

    def add_issue(
        self, issue_id, severity, problem_text, solution_text=None,
        affected_objects=None, solution_type=None,
        solution_url=None, solution_url_text=None, solution_component_name=None,
        solution_download_rpc_agent=False, execution_command=None
    ):
        """Add issue constructed from given problem and solution to report

        :rtype: None
        """
        issue = Issue(
            issue_id, severity, problem_text, solution_text, affected_objects, solution_type,
            solution_url, solution_url_text, solution_component_name, solution_download_rpc_agent,
            execution_command=execution_command
        )
        self.add_issue_obj(issue)

    def add_issue_obj(self, issue):
        """Add issue to report

        :type issue: parallels.core.reports.model.issue.Issue
        """
        if issue not in self.issues:
            self.issues.append(issue)

    def has_errors(self):
        return self._has_issue_of_severity(Issue.SEVERITY_ERROR)

    def has_warnings(self):
        return self._has_issue_of_severity(Issue.SEVERITY_WARNING)

    def has_errors_or_warnings(self):
        return any(chain(
            [
                issue.severity == Issue.SEVERITY_ERROR or issue.severity == Issue.SEVERITY_WARNING
                for issue in self.issues
                ],
            [subreport.has_errors_or_warnings() for subreport in self.children]
        ))

    def has_issues(self):
        return any(chain(
            [len(self.issues) > 0],
            [subreport.has_issues() for subreport in self.children]
        ))

    def _has_issue_of_severity(self, severity):
        return any(chain(
            [issue.severity == severity for issue in self.issues],
            [subreport.has_errors() for subreport in self.children]
        ))
