"""AH-009 (FRAUD: reputation-weighted routing + adaptive audit rate).

Covers the audit-sampling half of the issue: per-wallet audit probability as
a function of tenure/reputation, a fleet-wide budget balancer that keeps the
realized audit rate anchored to a configured target, and passive tripwire
escalation (ADR-0018 §1, §6-7).
"""

import random

import pytest

from meshnet_contracts import LocalSolanaContracts
from meshnet_node.server import StubNodeServer
from meshnet_validator import AdaptiveAuditSampler, AuditRateConfig, ValidatorProcess, detect_output_tripwire

MODEL = "stub-model"


# ---- per-wallet base rate: newcomer high, veteran low, floor >= 2% ----


def test_newcomer_gets_elevated_audit_rate():
    "Newcomer gets elevated audit rate\n\nTags: general"

    sampler = AdaptiveAuditSampler()
    rate = sampler.wallet_base_rate(completed_job_count=0, reputation=1.0)
    assert 0.20 <= rate <= 0.30


def test_veteran_in_good_standing_floors_near_target():
    "Veteran in good standing floors near target\n\nTags: general"

    sampler = AdaptiveAuditSampler()
    rate = sampler.wallet_base_rate(completed_job_count=1000, reputation=1.0)
    assert rate == pytest.approx(0.02)


def test_veteran_rate_never_drops_below_floor():
    "Veteran rate never drops below floor\n\nTags: general"

    sampler = AdaptiveAuditSampler(AuditRateConfig(veteran_floor=0.02))
    rate = sampler.wallet_base_rate(completed_job_count=10_000, reputation=1.0)
    assert rate >= 0.02


def test_low_reputation_wallet_sampled_more_than_high_reputation_wallet():
    "Red (test-first item 1): a uniform sampler ignores reputation.\n\nTags: security, wallet"

    sampler = AdaptiveAuditSampler()
    low_rep_rate = sampler.sample_rate_for(completed_job_count=200, reputation=0.1)
    high_rep_rate = sampler.sample_rate_for(completed_job_count=200, reputation=1.0)
    assert low_rep_rate > high_rep_rate


def test_low_reputation_escalates_even_for_a_tenured_wallet():
    "Low reputation escalates even for a tenured wallet\n\nTags: security, wallet"

    sampler = AdaptiveAuditSampler()
    rate = sampler.wallet_base_rate(completed_job_count=1000, reputation=0.0)
    assert rate == pytest.approx(sampler.config.newcomer_rate)


# ---- fleet-wide budget balance ----


def test_fleet_wide_audit_rate_tracks_configured_target_within_one_point():
    "Test-first item 2: over >=1000 requests with a fixed seed and a mixed\n\nTags: general"

    sampler = AdaptiveAuditSampler(random_seed=1234)
    rng = random.Random(99)

    audited = 0
    total = 6000
    for _ in range(total):
        roll = rng.random()
        if roll < 0.7:
            wallet = dict(completed_job_count=800, reputation=1.0)  # veteran
        elif roll < 0.9:
            wallet = dict(completed_job_count=150, reputation=0.9)  # mid-tenure
        else:
            wallet = dict(completed_job_count=0, reputation=1.0)  # newcomer
        if sampler.should_audit(**wallet):
            audited += 1

    measured_rate = audited / total
    assert abs(measured_rate - sampler.config.target_rate) <= 0.01


def test_fleet_wide_audit_rate_respects_custom_target():
    "Fleet wide audit rate respects custom target\n\nTags: general"

    sampler = AdaptiveAuditSampler(AuditRateConfig(target_rate=0.10), random_seed=42)
    audited = sum(
        1
        for _ in range(2000)
        if sampler.should_audit(completed_job_count=800, reputation=1.0)
    )
    measured_rate = audited / 2000
    assert abs(measured_rate - 0.10) <= 0.01


def test_sampling_is_deterministic_for_a_fixed_seed():
    "Sampling is deterministic for a fixed seed\n\nTags: general"

    sampler_a = AdaptiveAuditSampler(random_seed=7)
    sampler_b = AdaptiveAuditSampler(random_seed=7)
    decisions_a = [sampler_a.should_audit(completed_job_count=0, reputation=1.0) for _ in range(200)]
    decisions_b = [sampler_b.should_audit(completed_job_count=0, reputation=1.0) for _ in range(200)]
    assert decisions_a == decisions_b


# ---- passive tripwires bump rate only ----


def test_tripwire_flag_bumps_audit_rate_for_that_wallet():
    "Tripwire flag bumps audit rate for that wallet\n\nTags: security, wallet"

    sampler = AdaptiveAuditSampler()
    normal_rate = sampler.sample_rate_for(completed_job_count=800, reputation=1.0, tripwire=False)
    flagged_rate = sampler.sample_rate_for(completed_job_count=800, reputation=1.0, tripwire=True)
    assert flagged_rate > normal_rate


def test_tripwire_does_not_change_other_wallets_rate():
    "A tripwire hit must never leak the multiplier into the shared\n\nTags: security, wallet"

    flagged = AdaptiveAuditSampler(random_seed=5)
    flagged.should_audit(completed_job_count=800, reputation=1.0, tripwire=True)

    plain = AdaptiveAuditSampler(random_seed=5)
    plain.should_audit(completed_job_count=800, reputation=1.0, tripwire=False)

    after_flagged = flagged.sample_rate_for(completed_job_count=800, reputation=1.0)
    after_plain = plain.sample_rate_for(completed_job_count=800, reputation=1.0)
    assert after_flagged == pytest.approx(after_plain)


def test_detect_output_tripwire_flags_repetition_loop():
    "Detect output tripwire flags repetition loop\n\nTags: general"

    degenerate = " ".join(["loop"] * 20)
    assert detect_output_tripwire(degenerate) is True


def test_detect_output_tripwire_flags_empty_output():
    "Detect output tripwire flags empty output\n\nTags: general"

    assert detect_output_tripwire("") is True


def test_detect_output_tripwire_passes_normal_prose():
    "Detect output tripwire passes normal prose\n\nTags: general"

    normal = "The quick brown fox jumps over the lazy dog near the riverbank."
    assert detect_output_tripwire(normal) is False


# ---- ValidatorProcess wiring: reputation-weighted sampling end to end ----


@pytest.fixture
def reference_node():
    node = StubNodeServer(shard_start=0, shard_end=31, is_last_shard=True)
    port = node.start()
    yield f"http://127.0.0.1:{port}"
    node.stop()


def _record_event(contracts, session_id: str, wallet: str) -> None:
    contracts.validation.record_completed_inference(
        session_id=session_id,
        model=MODEL,
        messages=[{"role": "user", "content": "2+2"}],
        observed_output="4",
        route_nodes=[{"wallet_address": wallet, "shard_end": 31}],
    )


def test_validator_uses_audit_sampler_when_configured(reference_node):
    "A flagged low-reputation wallet gets audited far more often than a\n\nTags: general"

    contracts = LocalSolanaContracts()
    contracts.registry.record_completed_jobs("wallet-veteran", 800)
    for _ in range(9):
        contracts.registry.record_audit_outcome("wallet-shady", passed=False)  # reputation -> 0.0

    trials = 400
    shady_audits = 0
    veteran_audits = 0
    for i in range(trials):
        sampler = AdaptiveAuditSampler(random_seed=i)
        validator = ValidatorProcess(
            contracts=contracts,
            reference_node_url=reference_node,
            audit_sampler=sampler,
            random_seed=i,
        )
        shady_audits += int(validator._should_sample(_FakeEvent("wallet-shady")))
        veteran_audits += int(validator._should_sample(_FakeEvent("wallet-veteran")))

    assert shady_audits > veteran_audits


class _FakeEvent:
    def __init__(self, wallet: str) -> None:
        self.route_nodes = [{"wallet_address": wallet, "shard_end": 31}]
        self.observed_output = "4"
