"""Agreement tests: pyfitparsernative vs garmin-fit-sdk reference."""

from .conftest import (
    SAMPLE_FIT,
    SESSION_NUMERIC_FIELDS,
    garmin_sdk_parse,
    messages_by_type,
    message_type_counts,
)


def test_message_types_match_reference():
    """pyfitparsernative returns the same set of message types as garmin-fit-sdk."""
    from pyfitparsernative import parse_fit_file

    native_types = set(message_type_counts(parse_fit_file(SAMPLE_FIT)))
    ref_types = set(message_type_counts(garmin_sdk_parse(SAMPLE_FIT)))

    assert native_types == ref_types, (
        f"Extra in native: {native_types - ref_types}\n"
        f"Missing from native: {ref_types - native_types}"
    )


def test_record_counts_match_reference():
    """pyfitparsernative returns the same number of messages per type as garmin-fit-sdk."""
    from pyfitparsernative import parse_fit_file

    native_counts = message_type_counts(parse_fit_file(SAMPLE_FIT))
    ref_counts = message_type_counts(garmin_sdk_parse(SAMPLE_FIT))

    for msg_type in ref_counts:
        assert msg_type in native_counts, (
            f"'{msg_type}' present in reference but missing from native"
        )
        assert native_counts[msg_type] == ref_counts[msg_type], (
            f"'{msg_type}': native={native_counts[msg_type]}, "
            f"reference={ref_counts[msg_type]}"
        )


def test_session_numeric_fields_match_reference():
    """Session numeric fields from pyfitparsernative equal garmin-fit-sdk values."""
    from pyfitparsernative import parse_fit_file

    native_session = messages_by_type(parse_fit_file(SAMPLE_FIT))["session"][0]
    ref_session = messages_by_type(garmin_sdk_parse(SAMPLE_FIT))["session"][0]

    for field in SESSION_NUMERIC_FIELDS:
        assert native_session[field] == ref_session[field], (
            f"'{field}': native={native_session[field]}, "
            f"reference={ref_session[field]}"
        )


def test_session_timestamp_matches_reference():
    """Session timestamp from pyfitparsernative matches garmin-fit-sdk (modulo representation)."""
    from pyfitparsernative import parse_fit_file

    native_session = messages_by_type(parse_fit_file(SAMPLE_FIT))["session"][0]
    ref_session = messages_by_type(garmin_sdk_parse(SAMPLE_FIT))["session"][0]

    # native returns a naive ISO string; garmin-fit-sdk returns a tz-aware datetime.
    assert isinstance(native_session["timestamp"], str)
    ref_iso = ref_session["timestamp"].strftime("%Y-%m-%dT%H:%M:%S")
    assert native_session["timestamp"] == ref_iso, (
        f"native={native_session['timestamp']}, reference={ref_iso}"
    )


def test_record_timestamps_match_reference():
    """Record timestamps from pyfitparsernative match garmin-fit-sdk for first, last, and a mid record."""
    from pyfitparsernative import parse_fit_file

    native_records = messages_by_type(parse_fit_file(SAMPLE_FIT))["record"]
    ref_records = messages_by_type(garmin_sdk_parse(SAMPLE_FIT))["record"]

    indices = [0, len(ref_records) // 2, -1]
    for i in indices:
        ref_iso = ref_records[i]["timestamp"].strftime("%Y-%m-%dT%H:%M:%S")
        assert native_records[i]["timestamp"] == ref_iso, (
            f"record[{i}]: native={native_records[i]['timestamp']}, reference={ref_iso}"
        )


def test_record_fields_present_in_native():
    """Every field present in a garmin-fit-sdk record message is also in pyfitparsernative."""
    from pyfitparsernative import parse_fit_file

    native_records = messages_by_type(parse_fit_file(SAMPLE_FIT))["record"]
    ref_records = messages_by_type(garmin_sdk_parse(SAMPLE_FIT))["record"]

    # Check field names on the first record as a representative sample.
    # Exclude integer-keyed fields: garmin-fit-sdk returns developer fields using
    # their numeric field-definition numbers as integer dict keys; pyfitparsernative
    # exposes them as string keys (e.g. 'unknown_field_61').  That is a structural
    # difference, not a data loss.
    # Exclude timestamp fields (different representations).
    ref_fields = {
        k for k in ref_records[0]
        if isinstance(k, str) and not hasattr(ref_records[0][k], "isoformat")
    }
    native_fields = set(native_records[0].keys())

    missing = ref_fields - native_fields
    assert not missing, f"Fields present in reference but missing from native: {missing}"
