#!/usr/bin/env python3
"""Exits with failure message if, for any single class, coverage goes down.

Arguments: two Jacoco coverage reports (.csv files).
"""

# import argparse
import csv
import sys
from pathlib import Path

PROGRAM = Path(__file__).name

DEBUG = False


def main():
    """Exit with failure message if, for any single class, coverage goes down."""
    num_args = len(sys.argv) - 1
    if num_args != 2:
        print(f"{PROGRAM} received {num_args} arguments, expected 2: {sys.argv[1:]}")
        sys.exit(2)

    old_coverage = read_jacoco_csv_to_map(sys.argv[1])
    new_coverage = read_jacoco_csv_to_map(sys.argv[2])

    failed = False
    for fq_classname, new_instruction_cov in sorted(new_coverage.items()):
        if fq_classname not in old_coverage:
            continue
        old_instruction_cov = old_coverage[fq_classname]
        if old_instruction_cov == new_instruction_cov:
            continue
        if DEBUG:
            print(fq_classname, old_instruction_cov, new_instruction_cov, file=sys.stderr)
        if not check_coverage_ratio(fq_classname, old_instruction_cov, new_instruction_cov):
            failed = True

    if failed:
        sys.exit(1)


def read_jacoco_csv_to_map(filename: str) -> dict[str, tuple[int, int]]:
    """Read a Jacoco file.

    Returns:
        a map from class name to (instruction_missed, instruction_covered).
    """
    result = {}
    with Path.open(Path(filename)) as csvfile:
        dict_reader = csv.DictReader(csvfile)
        for row in dict_reader:
            fq_classname = row["PACKAGE"] + "." + row["CLASS"]
            result[fq_classname] = (int(row["INSTRUCTION_MISSED"]), int(row["INSTRUCTION_COVERED"]))
    return result


def check_coverage_ratio(
    class_name: str, old_pair: tuple[int, int], new_pair: tuple[int, int]
) -> bool:
    """Return false if the coverage decreased, true otherwise.

    Returns:
         false if the coverage decreased, true otherwise.
    """
    old_missed = old_pair[0]
    old_covered = old_pair[1]
    old_denominator = old_covered + old_missed
    if old_denominator == 0:
        return True
    old_ratio = old_covered / old_denominator
    new_missed = new_pair[0]
    new_covered = new_pair[1]
    new_denominator = new_covered + new_missed
    if new_denominator == 0:
        return True
    new_ratio = new_covered / new_denominator
    if new_ratio < old_ratio:
        print(
            f"Coverage of {class_name} fell "
            f"from {old_covered}/{old_denominator} "
            f"to {new_covered}/{new_denominator}"
        )
        return False
    return True


if __name__ == "__main__":
    main()
