use strict;
use warnings;

use Test::More;
use Test::Exception;
use Scalar::Util qw(looks_like_number);

use GDPR::IAB::TCFv2;
use GDPR::IAB::TCFv2::BitUtils;

# These flags decide whether the BitUtils helpers take the fast
# pack 'S>' / 'Q>' path or the Math::BigInt fallback.  On modern
# Perls both flags are true; on Perl < 5.10 (e.g. 5.8.9 on FreeBSD,
# CPAN Testers report 281cd334-...) the fallback is the only path.
#
# Forcing both flags off here exercises the fallback unconditionally
# so the regression is testable on any Perl.
#
# Regression: the fallback used to return raw Math::BigInt blessed
# objects, which then propagated into TO_JSON output and made any
# JSON encoder without `convert_blessed` croak with
# "encountered object 'Math::BigInt=HASH(...)'...".

my $tc_string =
  'CLcVDxRMWfGmWAVAHCENAXCkAKDAADnAABRgA5mdfCKZuYJez-NQm0TBMYA4oCAAGQYIAAAAAAEAIAEgAA';

subtest 'fallback path returns plain scalars (no blessed Math::BigInt)' =>
  sub {
    local $GDPR::IAB::TCFv2::BitUtils::CAN_PACK_QUADS       = 0;
    local $GDPR::IAB::TCFv2::BitUtils::CAN_FORCE_BIG_ENDIAN = 0;

    my $consent;
    lives_ok { $consent = GDPR::IAB::TCFv2->Parse($tc_string) }
    'Parse succeeds with both fast paths disabled';

    for my $accessor (
        qw< cmp_id cmp_version vendor_list_version policy_version
        max_vendor_id_consent max_vendor_id_legitimate_interest
        created last_updated >
      )
    {
        my $value = $consent->$accessor;
        is ref($value), '',
          "$accessor returns a plain scalar (got "
          . ( ref($value) || 'scalar' ) . ')';
    }
  };

subtest 'fallback path returns correct 36-bit timestamp values' => sub {

    # Regression for CPAN Testers report 3e3e8fca-4988-11f1-843a-...
    # on armv6l-linux (Perl 5.42.2 with use64bitint=undef, ivsize=4).
    #
    # TCF v2 stores Created/LastUpdated as a 36-bit deciseconds-since-epoch
    # field, which doesn't fit in a 32-bit signed IV.  When the BigInt
    # fallback is in use, get_uint36 returns the value as an NV via
    # Math::BigInt->numify -- but the file-level `use integer` in
    # GDPR::IAB::TCFv2 was forcing the subsequent `/ 10` and `% 10` to
    # coerce that NV back to a 32-bit IV, overflowing and producing
    # `created => 0` and `nanoseconds => -100000000` on armv6l.
    #
    # This subtest forces the fallback path on every Perl so the bug is
    # caught on 64-bit smokers too.

    local $GDPR::IAB::TCFv2::BitUtils::CAN_PACK_QUADS       = 0;
    local $GDPR::IAB::TCFv2::BitUtils::CAN_FORCE_BIG_ENDIAN = 0;

    my $consent = GDPR::IAB::TCFv2->Parse($tc_string);

    is $consent->created, 1228644257,
      'created returns the correct epoch in scalar context';
    is $consent->last_updated, 1326215413,
      'last_updated returns the correct epoch in scalar context';

    {
        my ( $sec, $nsec ) = $consent->created;
        is $sec,  1228644257, 'created seconds in list context';
        is $nsec, 700000000,  'created nanoseconds in list context';
    }
    {
        my ( $sec, $nsec ) = $consent->last_updated;
        is $sec,  1326215413, 'last_updated seconds in list context';
        is $nsec, 400000000,  'last_updated nanoseconds in list context';
    }
};

subtest 'fallback values JSON-encode without convert_blessed' => sub {
    local $GDPR::IAB::TCFv2::BitUtils::CAN_PACK_QUADS       = 0;
    local $GDPR::IAB::TCFv2::BitUtils::CAN_FORCE_BIG_ENDIAN = 0;

    my $json_class =
        eval { require JSON;     1 } ? 'JSON'
      : eval { require JSON::PP; 1 } ? 'JSON::PP'
      :                                undef;
    plan skip_all => 'no JSON encoder available' unless $json_class;

    my $consent = GDPR::IAB::TCFv2->Parse($tc_string);

    # Deliberately do NOT enable convert_blessed.  A blessed Math::BigInt
    # leaking through TO_JSON would croak here.
    my $encoder = $json_class->new;
    my $output;
    lives_ok { $output = $encoder->encode( $consent->TO_JSON ) }
    'TO_JSON encodes cleanly without convert_blessed';

    # Decode and assert structurally.  The previous regex-on-JSON form
    # (qr/"cmp_id"\s*:\s*\d+/) made the test sensitive to encoder
    # whitespace and number-vs-string serialization quirks across
    # JSON::XS / JSON::PP / Perl versions.
    my $decoded = $encoder->decode($output);
    ok( looks_like_number( $decoded->{cmp_id} ),
        'cmp_id round-trips as a JSON number'
    );
    ok( looks_like_number( $decoded->{vendor_list_version} ),
        'vendor_list_version round-trips as a JSON number'
    );
};

done_testing;
