"""
Tests for headless_oracle.verify.

Signs payloads with PyNaCl (same library the SDK uses for key parsing) to
produce valid receipts, then runs them through verify() to confirm round-trip
correctness.  This mirrors the JS SDK's test strategy: sign with noble/ed25519,
verify with Web Crypto.
"""

import json
import time
import uuid
from datetime import datetime, timezone, timedelta

import pytest
from nacl.signing import SigningKey
from nacl.encoding import HexEncoder

from headless_oracle.verify import verify, VerifyResult, _canonical_payload

# ── Fixtures ──────────────────────────────────────────────────────────────────

@pytest.fixture(scope="session")
def keypair():
    """Generate a fresh Ed25519 keypair for this test session."""
    signing_key = SigningKey.generate()
    verify_key = signing_key.verify_key
    private_hex = signing_key.encode(encoder=HexEncoder).decode()
    public_hex = verify_key.encode(encoder=HexEncoder).decode()
    return signing_key, public_hex


def make_receipt(signing_key, *, mic="XNYS", status="OPEN", source="SCHEDULE",
                 receipt_mode="live", offset_seconds=60, is_health=False,
                 public_key_id="key_test_v1", reason=None):
    """Build and sign a syntactically valid receipt.

    reason: if provided (OVERRIDE receipts), it is included in the signed payload
    before signing — matching the Oracle's canonical_payload_spec for override_fields.
    """
    now = datetime.now(tz=timezone.utc)
    expires_at = now + timedelta(seconds=offset_seconds)

    if is_health:
        payload = {
            "receipt_id": str(uuid.uuid4()),
            "issued_at": now.isoformat(),
            "expires_at": expires_at.isoformat(),
            "issuer": "headlessoracle.com",
            "status": "OK",
            "source": "SYSTEM",
            "public_key_id": public_key_id,
        }
    else:
        payload = {
            "receipt_id": str(uuid.uuid4()),
            "issued_at": now.isoformat(),
            "expires_at": expires_at.isoformat(),
            "issuer": "headlessoracle.com",
            "mic": mic,
            "status": status,
            "source": source,
            "receipt_mode": receipt_mode,
            "schema_version": "v5.0",
            "public_key_id": public_key_id,
        }
        # reason is part of the signed payload for OVERRIDE receipts
        if reason is not None:
            payload["reason"] = reason

    canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
    signed = signing_key.sign(canonical, encoder=HexEncoder)
    sig_hex = signed.signature.decode()

    return {**payload, "signature": sig_hex}


# ── Happy path ────────────────────────────────────────────────────────────────

def test_valid_open_receipt(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, status="OPEN")
    result = verify(receipt, public_key=public_hex)
    assert result.valid is True
    assert result.reason is None


def test_valid_closed_receipt(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, status="CLOSED")
    result = verify(receipt, public_key=public_hex)
    assert result.valid is True


def test_valid_override_receipt(keypair):
    signing_key, public_hex = keypair
    # reason must be in the signed payload — pass it to make_receipt, not appended after
    receipt = make_receipt(signing_key, status="HALTED", source="OVERRIDE",
                           reason="NYSE circuit breaker L1")
    result = verify(receipt, public_key=public_hex)
    assert result.valid is True
    assert receipt["reason"] == "NYSE circuit breaker L1"


def test_valid_health_receipt(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, is_health=True)
    result = verify(receipt, public_key=public_hex)
    assert result.valid is True


def test_valid_demo_receipt(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, receipt_mode="demo")
    result = verify(receipt, public_key=public_hex)
    assert result.valid is True


# ── Failure cases ─────────────────────────────────────────────────────────────

def test_missing_fields_returns_false(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key)
    del receipt["signature"]
    result = verify(receipt, public_key=public_hex)
    assert result.valid is False
    assert result.reason == "MISSING_FIELDS"


def test_missing_issuer_returns_missing_fields(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key)
    del receipt["issuer"]
    result = verify(receipt, public_key=public_hex)
    assert result.valid is False
    assert result.reason == "MISSING_FIELDS"


def test_expired_receipt_returns_expired(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, offset_seconds=-10)
    result = verify(receipt, public_key=public_hex)
    assert result.valid is False
    assert result.reason == "EXPIRED"


def test_tampered_status_invalid_signature(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, status="CLOSED")
    receipt["status"] = "OPEN"  # tamper after signing
    result = verify(receipt, public_key=public_hex)
    assert result.valid is False
    assert result.reason == "INVALID_SIGNATURE"


def test_wrong_public_key_invalid_signature(keypair):
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key)
    wrong_key = SigningKey.generate().verify_key.encode(encoder=HexEncoder).decode()
    result = verify(receipt, public_key=wrong_key)
    assert result.valid is False
    assert result.reason == "INVALID_SIGNATURE"


def test_invalid_key_format():
    receipt = {
        "receipt_id": str(uuid.uuid4()),
        "issued_at": datetime.now(tz=timezone.utc).isoformat(),
        "expires_at": (datetime.now(tz=timezone.utc) + timedelta(seconds=60)).isoformat(),
        "issuer": "headlessoracle.com",
        "mic": "XNYS",
        "status": "OPEN",
        "source": "SCHEDULE",
        "receipt_mode": "live",
        "schema_version": "v5.0",
        "public_key_id": "key_test_v1",
        "signature": "aabbccdd",
    }
    result = verify(receipt, public_key="not_valid_hex_key_at_all")
    assert result.valid is False
    assert result.reason == "INVALID_KEY_FORMAT"


def test_future_expiry_not_expired_yet(keypair):
    """A receipt expiring in 30 seconds is not expired."""
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, offset_seconds=30)
    result = verify(receipt, public_key=public_hex)
    assert result.valid is True


def test_now_override_makes_receipt_expired(keypair):
    """now= override lets consumers simulate time travel in tests."""
    signing_key, public_hex = keypair
    receipt = make_receipt(signing_key, offset_seconds=60)
    far_future = time.time() + 3600
    result = verify(receipt, public_key=public_hex, now=far_future)
    assert result.valid is False
    assert result.reason == "EXPIRED"
