# -------------------------------------------------------------------------
#
#  Part of the CodeChecker project, under the Apache License v2.0 with
#  LLVM Exceptions. See LICENSE for license information.
#  SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# -------------------------------------------------------------------------

import logging
import os
import xml.etree.ElementTree as ET

from typing import Dict, List, Optional

from codechecker_report_converter.report import File, get_or_create_file, \
    Report

from ..analyzer_result import AnalyzerResultBase


LOG = logging.getLogger('report-converter')


class AnalyzerResult(AnalyzerResultBase):
    """ Transform analyzer result of Roslynator. """

    TOOL_NAME = 'roslynator'
    NAME = 'Roslynator.DotNet.Cli'
    URL = 'https://github.com/JosefPihrt/Roslynator' \
        + '#roslynator-command-line-tool-'

    def __init__(self):
        super(AnalyzerResult, self).__init__()
        self.__file_cache: Dict[str, File] = {}

    def get_reports(
        self,
        file_path: str
    ) -> List[Report]:
        """ Get reports from the given analyzer result. """
        reports: List[Report] = []

        root = self.__parse_analyzer_result(file_path)
        if root is None:
            return reports

        for diag in root.findall('./CodeAnalysis/Projects/*/Diagnostics/*'):
            report = self.__parse_diag(diag, file_path)
            if report is not None:
                reports.append(report)

        return reports

    def __parse_analyzer_result(
        self,
        analyzer_result: str
    ) -> Optional[ET.Element]:
        """ Parse the given analyzer result xml file.

        Returns the root element of the parsed tree or None if something goes
        wrong.
        """
        try:
            tree = ET.parse(analyzer_result)
            return tree.getroot()
        except OSError:
            LOG.error("Analyzer result does not exist: %s", analyzer_result)
            return None
        except ET.ParseError:
            LOG.error("Failed to parse the given analyzer result '%s'. Please "
                      "give a valid xml file with messages generated by "
                      "Roslynator.", analyzer_result)
            return None

    def __parse_diag(
        self,
        diag,
        input_file_path: str
    ) -> Optional[Report]:
        """ Parse the given Roslynator diagnostic

        Returns the Report from the parsed diagnostic or None if something goes
        wrong.
        """
        id = diag.attrib.get('Id')

        filePathElement = diag.find('FilePath')
        if filePathElement is None:
            LOG.warning("Diagnostic does not belong to a file: %s", id)
            return None

        source_file_path = os.path.join(os.path.dirname(input_file_path),
                                        filePathElement.text)
        if not os.path.exists(source_file_path):
            LOG.warning("Source file does not exist: %s", source_file_path)
            return None

        message = diag.find('Message').text
        location = diag.find('Location')
        line = location.attrib.get('Line')
        column = location.attrib.get('Character')

        return Report(
            get_or_create_file(source_file_path, self.__file_cache),
            int(line),
            int(column),
            message,
            id
        )
