"""
Agent Permissions – Spending limits, service filters, and counterparty controls.

All permissions are optional. No permissions = unlimited access.
"""
from __future__ import annotations
import json
import os
import time
from dataclasses import dataclass, asdict, field


@dataclass
class SpendingLimits:
    """Spending limits for an agent."""
    per_transaction_usd: float = 0.0  # 0 = unlimited
    daily_usd: float = 0.0           # 0 = unlimited
    monthly_usd: float = 0.0         # 0 = unlimited


@dataclass
class AgentPermissions:
    """Full permission set for an agent."""
    spending_limits: SpendingLimits = field(default_factory=SpendingLimits)
    allowed_services: list[str] = field(default_factory=list)   # empty = all allowed
    blocked_services: list[str] = field(default_factory=list)
    whitelist: list[str] = field(default_factory=list)          # empty = all allowed
    blacklist: list[str] = field(default_factory=list)


@dataclass
class PermissionResult:
    """Result of a permission check."""
    allowed: bool
    reason: str = ""


class PermissionEngine:
    """
    Manages agent permissions and spending limits.

    Usage:
        engine = PermissionEngine()
        engine.set_permissions("0xAgent...", AgentPermissions(
            spending_limits=SpendingLimits(per_transaction_usd=10.0, daily_usd=100.0),
            blocked_services=["gambling"],
            blacklist=["0xScammer..."],
        ))
        result = engine.check_permission("0xAgent...", "0xRecipient...", 5.0, "translation")
    """

    def __init__(self, storage_path: str = ""):
        if not storage_path:
            storage_path = os.path.join(
                os.path.dirname(__file__), "..", "permissions.json"
            )
        self._storage_path = os.path.abspath(storage_path)
        self._permissions: dict[str, dict] = self._load()
        self._spending_log: dict[str, list] = {}  # wallet -> [(timestamp, amount)]

    def _load(self) -> dict:
        if os.path.exists(self._storage_path):
            with open(self._storage_path, "r") as f:
                return json.load(f)
        return {}

    def _save(self):
        with open(self._storage_path, "w") as f:
            json.dump(self._permissions, f, indent=2, ensure_ascii=False)

    def set_permissions(self, wallet: str, permissions: AgentPermissions):
        """Set permissions for an agent."""
        self._permissions[wallet] = asdict(permissions)
        self._save()

    def get_permissions(self, wallet: str) -> AgentPermissions | None:
        """Get permissions for an agent. Returns None if no permissions set."""
        if wallet not in self._permissions:
            return None
        data = self._permissions[wallet]
        return AgentPermissions(
            spending_limits=SpendingLimits(**data.get("spending_limits", {})),
            allowed_services=data.get("allowed_services", []),
            blocked_services=data.get("blocked_services", []),
            whitelist=data.get("whitelist", []),
            blacklist=data.get("blacklist", []),
        )

    def remove_permissions(self, wallet: str):
        """Remove all permissions for an agent (back to unlimited)."""
        self._permissions.pop(wallet, None)
        self._save()

    def check_permission(
        self,
        sender: str,
        recipient: str = "",
        amount: float = 0.0,
        service_type: str = "",
    ) -> PermissionResult:
        """
        Check if a transaction is allowed.

        Returns PermissionResult with allowed=True if no restrictions apply.
        """
        if sender not in self._permissions:
            return PermissionResult(allowed=True)

        perms = self.get_permissions(sender)

        # Check blacklist
        if perms.blacklist and recipient.lower() in [b.lower() for b in perms.blacklist]:
            return PermissionResult(allowed=False, reason="Recipient is blacklisted")

        # Check whitelist
        if perms.whitelist and recipient.lower() not in [w.lower() for w in perms.whitelist]:
            return PermissionResult(allowed=False, reason="Recipient is not whitelisted")

        # Check blocked services
        if perms.blocked_services and service_type:
            if service_type.lower() in [s.lower() for s in perms.blocked_services]:
                return PermissionResult(allowed=False, reason=f"Service '{service_type}' is blocked")

        # Check allowed services
        if perms.allowed_services and service_type:
            if service_type.lower() not in [s.lower() for s in perms.allowed_services]:
                return PermissionResult(allowed=False, reason=f"Service '{service_type}' is not allowed")

        # Check spending limits
        limits = perms.spending_limits

        # Per-transaction limit
        if limits.per_transaction_usd > 0 and amount > limits.per_transaction_usd:
            return PermissionResult(
                allowed=False,
                reason=f"Amount ${amount} exceeds per-transaction limit of ${limits.per_transaction_usd}",
            )

        # Daily limit
        if limits.daily_usd > 0:
            daily_spent = self._get_spent(sender, hours=24)
            if daily_spent + amount > limits.daily_usd:
                return PermissionResult(
                    allowed=False,
                    reason=f"Would exceed daily limit of ${limits.daily_usd} (spent: ${daily_spent:.2f})",
                )

        # Monthly limit
        if limits.monthly_usd > 0:
            monthly_spent = self._get_spent(sender, hours=24 * 30)
            if monthly_spent + amount > limits.monthly_usd:
                return PermissionResult(
                    allowed=False,
                    reason=f"Would exceed monthly limit of ${limits.monthly_usd} (spent: ${monthly_spent:.2f})",
                )

        return PermissionResult(allowed=True)

    def record_spending(self, wallet: str, amount: float):
        """Record a spending event for limit tracking."""
        if wallet not in self._spending_log:
            self._spending_log[wallet] = []
        self._spending_log[wallet].append((time.time(), amount))

    def _get_spent(self, wallet: str, hours: float) -> float:
        """Get total spent in the last N hours."""
        if wallet not in self._spending_log:
            return 0.0
        cutoff = time.time() - (hours * 3600)
        return sum(
            amount for ts, amount in self._spending_log[wallet]
            if ts >= cutoff
        )
