"""Issue 05 (A1/A5) tests: strike/ban/reputation state survives tracker restart.

Registry wallet state was RAM-only (`_LocalContractState.registry`) — a tracker
restart wiped strikes, bans, and job counts. These tests pin the event-sourced
SQLite persistence (same replication model as BillingLedger / AccountStore)
per ADR-0016 §4 and ADR-0018 §6.
"""

import json
import urllib.error
import urllib.request

import pytest

from meshnet_contracts import LocalSolanaContracts, RegistryWallet
from meshnet_tracker.server import TrackerServer


def _post_json(url: str, payload: dict) -> dict:
    data = json.dumps(payload).encode()
    req = urllib.request.Request(
        url, data=data, headers={"Content-Type": "application/json"}, method="POST"
    )
    with urllib.request.urlopen(req) as r:
        return json.loads(r.read())


# ---- schema: new reputation fields ----


def test_registry_wallet_has_reputation_fields():
    "Registry wallet has reputation fields\n\nTags: security, wallet"
    wallet = RegistryWallet()
    assert wallet.reputation == 1.0
    assert wallet.last_audit_ts is None


# ---- persistence across restart ----


def test_strike_survives_restart(tmp_path):
    "Strike survives restart\n\nTags: general"
    db = str(tmp_path / "registry.sqlite")
    contracts = LocalSolanaContracts(registry_db=db)
    contracts.registry.record_strike("wallet-a")
    contracts.save_to_db()

    reopened = LocalSolanaContracts(registry_db=db)
    assert reopened.registry.get_wallet("wallet-a").strike_count == 1


def test_ban_stake_and_jobs_survive_restart(tmp_path):
    "Ban stake and jobs survive restart\n\nTags: general"
    db = str(tmp_path / "registry.sqlite")
    contracts = LocalSolanaContracts(registry_db=db)
    contracts.registry.submit_stake("wallet-b", 500)
    contracts.registry.submit_slash_proof(
        wallet_address="wallet-b", slash_amount=200, strike_threshold=1
    )
    contracts.registry.record_completed_job("wallet-b")
    contracts.save_to_db()

    reopened = LocalSolanaContracts(registry_db=db)
    wallet = reopened.registry.get_wallet("wallet-b")
    assert wallet.banned is True
    assert wallet.strike_count == 1
    assert wallet.stake_balance == 300
    assert wallet.completed_job_count == 1
    # list_wallets must reflect persisted state
    assert "wallet-b" in reopened.registry.list_wallets()


def test_reputation_and_audit_ts_survive_restart(tmp_path):
    "Reputation and audit ts survive restart\n\nTags: general"
    db = str(tmp_path / "registry.sqlite")
    contracts = LocalSolanaContracts(registry_db=db)
    contracts.registry.set_reputation("wallet-c", 0.8)
    contracts.registry.record_audit("wallet-c", ts=1234.5)
    contracts.save_to_db()

    reopened = LocalSolanaContracts(registry_db=db)
    wallet = reopened.registry.get_wallet("wallet-c")
    assert wallet.reputation == pytest.approx(0.8)
    assert wallet.last_audit_ts == pytest.approx(1234.5)


# ---- event replication (multi-tracker gossip / future Raft) ----


def test_registry_events_replicate_between_instances():
    "Registry events replicate between instances\n\nTags: general"
    a = LocalSolanaContracts()
    b = LocalSolanaContracts()
    a.registry.record_strike("wallet-d")
    a.registry.submit_stake("wallet-d", 100)

    events, cursor = a.registry_log.events_since(0)
    assert cursor == len(events) >= 2
    applied = b.registry_log.apply_events(events)
    assert applied == len(events)
    assert b.registry.get_wallet("wallet-d") == a.registry.get_wallet("wallet-d")

    # idempotent: re-applying the same events is a no-op
    assert b.registry_log.apply_events(events) == 0


# ---- banned wallet rejected at registration after restart ----


def test_banned_wallet_rejected_at_registration_after_restart(tmp_path):
    "Banned wallet rejected at registration after restart\n\nTags: security, wallet"
    db = str(tmp_path / "registry.sqlite")
    contracts = LocalSolanaContracts(registry_db=db)
    contracts.registry.ban_wallet("wallet-evil")
    contracts.save_to_db()

    tracker = TrackerServer(contracts=LocalSolanaContracts(registry_db=db))
    port = tracker.start()
    try:
        with pytest.raises(urllib.error.HTTPError) as excinfo:
            _post_json(
                f"http://127.0.0.1:{port}/v1/nodes/register",
                {
                    "endpoint": "http://127.0.0.1:7900",
                    "model": "test-model",
                    "num_layers": 4,
                    "shard_start": 0,
                    "shard_end": 3,
                    "wallet_address": "wallet-evil",
                },
            )
        assert excinfo.value.code == 403
    finally:
        tracker.stop()
