#!/usr/bin/python3
from argparse import ArgumentParser, FileType
from csv import reader
from itertools import zip_longest
from sys import exit
from unittest import TestCase


def parse_args():
    parser = ArgumentParser(
        description="Compare actual and expected catch22 test output"
    )
    parser.add_argument(
        "--ignore-extra",
        action="append",
        metavar="FEATURE",
        default=[],
        help="ignore an extra feature in the actual output; multiple-allowed",
    )
    parser.add_argument(
        "expected", type=FileType("r"), help="Expected output CSV file"
    )
    parser.add_argument(
        "actual", type=FileType("r"), help="Actual output CSV file"
    )
    return parser.parse_args()


def iter_data(csvfile, ignore=()):
    for line in reader(csvfile):
        # Ignore blank/empty lines
        if not line:
            continue

        # This CSV format adds insignificant whitespace (", " delimiter).
        # Remove it.
        result, feature, timeTaken = map(str.strip, line)

        # Convert numeric values.
        result = float(result)
        timeTaken = float(timeTaken)

        if feature not in ignore:
            yield result, feature, timeTaken


def main():
    args = parse_args()
    # Abuse TestCase to compare floats outside of the usual unittest framework:
    tc = TestCase()
    with args.expected as xfile, args.actual as afile:
        xdata = iter_data(xfile)
        adata = iter_data(afile, set(args.ignore_extra))
        # We ignore the third column (time taken).
        for i, (xdata, adata) in enumerate(
            zip_longest(xdata, adata), start=1
        ):
            if xdata is None:
                exit(f"Line {i}: At least one extra line: {adata!r}")
            xresult, xfeature, _ = xdata
            if adata is None:
                exit(
                    f"Line {i}: Output ended early; "
                    f"expected feature {xfeature!r}"
                )
            aresult, afeature, _ = adata
            if xfeature != afeature:
                exit(
                    f"Line {i}: Got feature {afeature!r}, "
                    f"expected {xfeature!r}"
                )
            try:
                tc.assertAlmostEqual(xresult, aresult)
            except AssertionError as exc:
                exit(
                    f"Line {i}, feature {xfeature!r}: got result "
                    f"{aresult}, expected {xresult}: {exc}"
                )


if __name__ == "__main__":
    exit(main())
