# -*- coding:utf-8 -*-

#  ************************** Copyrights and license ***************************
#
# This file is part of gcovr 8.3, a parsing and reporting tool for gcov.
# https://gcovr.com/en/8.3
#
# _____________________________________________________________________________
#
# Copyright (c) 2013-2025 the gcovr authors
# Copyright (c) 2013 Sandia Corporation.
# Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
# the U.S. Government retains certain rights in this software.
#
# This software is distributed under the 3-clause BSD License.
# For more information, see the README.rst file.
#
# ****************************************************************************

"""
Handle writing of LCOV files.

The LCOV format is described in https://github.com/linux-test-project/lcov/blob/07a1127c2b4390abf4a516e9763fb28a956a9ce4/man/geninfo.1#L989
and generated by https://github.com/linux-test-project/lcov/blob/07a1127c2b4390abf4a516e9763fb28a956a9ce4/bin/geninfo
"""

# cspell:ignore FNDA BRDA

from ...options import Options

from ...utils import force_unix_separator, get_md5_hexdigest, open_text_for_writing
from ...coverage import CoverageContainer


def write_report(
    covdata: CoverageContainer, output_file: str, options: Options
) -> None:
    """produce gcovr csv report"""

    with open_text_for_writing(output_file, "coverage.lcov") as fh:
        keys = covdata.sort_coverage(
            sort_key=options.sort_key,
            sort_reverse=options.sort_reverse,
            by_metric="branch" if options.sort_branches else "line",
        )
        if options.lcov_comment is not None:
            # #comment_string
            fh.write(f"#{options.lcov_comment}\n")
        # TN:<test name>
        fh.write(f"TN:{options.lcov_test_name}\n")

        def postfix() -> str:
            return f"_{lineno}" if len(linenos) > 1 else ""

        for key in keys:
            filecov = covdata[key]
            filename = force_unix_separator(filecov.filename)

            # SF:<path to the source file>
            fh.write(f"SF:{filename}\n")

            # This filename is generated in the JSON intermediate format
            if not options.lcov_format_v1 and not filename.endswith("<stdin>"):
                # VER:<version ID>
                # Generate md5 hash of file contents
                with open(filename, "rb") as file_handle:
                    contents = file_handle.read()
                fh.write(f"VER:{get_md5_hexdigest(contents)}\n")

            functions = 0
            function_hits = 0
            for function_name in sorted(filecov.functions):
                linenos = list(filecov.functions[function_name].count)
                functions += len(linenos)

                for lineno in sorted(linenos):
                    # FN:<line number of function start>,[<line number of function end>,]<function name>
                    fh.write(f"FN:{lineno},{function_name}{postfix()}\n")
                for lineno in sorted(filecov.functions[function_name].count):
                    count = filecov.functions[function_name].count[lineno]
                    if count:
                        function_hits += 1
                    # FNDA:<execution count>,<function name>
                    fh.write(f"FNDA:{count},{function_name}{postfix()}\n")
            # FNF:<number of functions found>
            fh.write(f"FNF:{functions}\n")
            # FNH:<number of function hit>
            fh.write(f"FNH:{function_hits}\n")

            branches = 0
            branch_hits = 0
            for lineno, linecov in filecov.lines.items():
                if linecov.is_reportable:
                    branches += len(linecov.branches)
                    for branchno, branchcov in linecov.branches.items():
                        if branchcov.count:
                            branch_hits += 1
                        # BRDA:<line_number>,[<exception>]<block>,<branch>,<taken>
                        fh.write(
                            f"BRDA:{lineno},{'e' if branchcov.throw else ''}{branchcov.source_block_id_or_0},{branchno},{branchcov.count if branchcov.count else '-'}\n"
                        )

            # BRF:<number of branches found>
            fh.write(f"BRF:{branches}\n")
            # BRH:<number of branches hit>
            fh.write(f"BRH:{branch_hits}\n")

            lines_covered = 0
            for lineno, linecov in filecov.lines.items():
                if linecov.is_reportable:
                    if linecov.count:
                        lines_covered += 1
                    # DA:<line number>,<execution count>[,<checksum>]
                    fh.write(f"DA:{lineno},{linecov.count},{linecov.md5}\n")

            stats = filecov.stats
            # LH:<number of lines with a non\-zero execution count>
            fh.write(f"LH:{stats.line.covered}\n")
            # LF:<number of instrumented lines>
            fh.write(f"LF:{stats.line.total}\n")

            # End of file section
            fh.write("end_of_record\n")
