"""Tests for the Permission Engine."""
import os
import time
import pytest
from swarmpay.permissions import (
    PermissionEngine, AgentPermissions, SpendingLimits, PermissionResult,
)

WALLET_A = "0x1111111111111111111111111111111111111111"
WALLET_B = "0x2222222222222222222222222222222222222222"
WALLET_C = "0x3333333333333333333333333333333333333333"


@pytest.fixture
def engine(tmp_path):
    return PermissionEngine(storage_path=str(tmp_path / "permissions.json"))


# ── No Permissions = Unlimited ──────────────────────────────

def test_no_permissions_allows_all(engine):
    result = engine.check_permission(WALLET_A, WALLET_B, 1000.0)
    assert result.allowed is True


# ── Per-Transaction Limit ───────────────────────────────────

def test_per_transaction_limit_pass(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(per_transaction_usd=10.0),
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 5.0)
    assert result.allowed is True


def test_per_transaction_limit_fail(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(per_transaction_usd=10.0),
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 15.0)
    assert result.allowed is False
    assert "per-transaction" in result.reason.lower()


# ── Daily Limit ─────────────────────────────────────────────

def test_daily_limit_pass(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(daily_usd=100.0),
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 50.0)
    assert result.allowed is True


def test_daily_limit_fail(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(daily_usd=100.0),
    ))
    # Record previous spending
    engine.record_spending(WALLET_A, 80.0)
    result = engine.check_permission(WALLET_A, WALLET_B, 30.0)
    assert result.allowed is False
    assert "daily" in result.reason.lower()


def test_daily_limit_accumulates(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(daily_usd=100.0),
    ))
    engine.record_spending(WALLET_A, 40.0)
    engine.record_spending(WALLET_A, 40.0)
    result = engine.check_permission(WALLET_A, WALLET_B, 25.0)
    assert result.allowed is False


# ── Monthly Limit ───────────────────────────────────────────

def test_monthly_limit_pass(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(monthly_usd=1000.0),
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 500.0)
    assert result.allowed is True


def test_monthly_limit_fail(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(monthly_usd=1000.0),
    ))
    engine.record_spending(WALLET_A, 900.0)
    result = engine.check_permission(WALLET_A, WALLET_B, 200.0)
    assert result.allowed is False
    assert "monthly" in result.reason.lower()


# ── Service Type Filters ────────────────────────────────────

def test_allowed_services_pass(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        allowed_services=["translation", "analysis"],
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 1.0, service_type="translation")
    assert result.allowed is True


def test_allowed_services_fail(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        allowed_services=["translation", "analysis"],
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 1.0, service_type="gambling")
    assert result.allowed is False
    assert "not allowed" in result.reason.lower()


def test_blocked_services_pass(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        blocked_services=["gambling"],
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 1.0, service_type="translation")
    assert result.allowed is True


def test_blocked_services_fail(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        blocked_services=["gambling"],
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 1.0, service_type="gambling")
    assert result.allowed is False
    assert "blocked" in result.reason.lower()


def test_service_type_case_insensitive(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        blocked_services=["Gambling"],
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 1.0, service_type="gambling")
    assert result.allowed is False


# ── Blacklist / Whitelist ───────────────────────────────────

def test_blacklist_blocks_recipient(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        blacklist=[WALLET_B],
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 1.0)
    assert result.allowed is False
    assert "blacklisted" in result.reason.lower()


def test_blacklist_allows_other_recipients(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        blacklist=[WALLET_B],
    ))
    result = engine.check_permission(WALLET_A, WALLET_C, 1.0)
    assert result.allowed is True


def test_whitelist_allows_listed(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        whitelist=[WALLET_B],
    ))
    result = engine.check_permission(WALLET_A, WALLET_B, 1.0)
    assert result.allowed is True


def test_whitelist_blocks_unlisted(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        whitelist=[WALLET_B],
    ))
    result = engine.check_permission(WALLET_A, WALLET_C, 1.0)
    assert result.allowed is False
    assert "not whitelisted" in result.reason.lower()


def test_empty_whitelist_allows_all(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        whitelist=[],
    ))
    result = engine.check_permission(WALLET_A, WALLET_C, 1.0)
    assert result.allowed is True


# ── Persistence ─────────────────────────────────────────────

def test_permissions_persistence(tmp_path):
    path = str(tmp_path / "perm.json")
    engine1 = PermissionEngine(storage_path=path)
    engine1.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(per_transaction_usd=5.0),
    ))
    # Reload
    engine2 = PermissionEngine(storage_path=path)
    perms = engine2.get_permissions(WALLET_A)
    assert perms is not None
    assert perms.spending_limits.per_transaction_usd == 5.0


def test_remove_permissions(engine):
    engine.set_permissions(WALLET_A, AgentPermissions(
        spending_limits=SpendingLimits(per_transaction_usd=5.0),
    ))
    engine.remove_permissions(WALLET_A)
    result = engine.check_permission(WALLET_A, WALLET_B, 100.0)
    assert result.allowed is True


def test_get_permissions_none(engine):
    assert engine.get_permissions(WALLET_A) is None
