"""Agent management for Sardis."""
from __future__ import annotations

import builtins
from datetime import UTC, datetime
from decimal import Decimal
from uuid import uuid4

from pydantic import BaseModel, Field


class SpendingLimits(BaseModel):
    """Spending limits configuration for an agent."""
    per_transaction: Decimal = Field(default=Decimal("100.00"))
    daily: Decimal = Field(default=Decimal("1000.00"))
    monthly: Decimal = Field(default=Decimal("10000.00"))
    total: Decimal = Field(default=Decimal("100000.00"))


class AgentPolicy(BaseModel):
    """Policy rules for an agent."""
    allowed_merchants: list[str] | None = None  # None = all allowed
    blocked_merchants: list[str] = Field(default_factory=list)
    allowed_categories: list[str] | None = None
    blocked_categories: list[str] = Field(default_factory=list)
    require_approval_above: Decimal | None = None
    auto_approve_below: Decimal = Field(default=Decimal("50.00"))


class Agent(BaseModel):
    """An AI agent with spending capabilities."""
    agent_id: str
    name: str
    description: str | None = None
    owner_id: str = "default"  # Organization/user who owns this agent
    wallet_id: str | None = None
    spending_limits: SpendingLimits = Field(default_factory=SpendingLimits)
    policy: AgentPolicy = Field(default_factory=AgentPolicy)
    api_key_hash: str | None = None  # For agent-specific API keys
    is_active: bool = True
    # KYA (Know Your Agent) fields
    kya_level: str = "none"      # none | basic | verified | attested
    kya_status: str = "pending"  # pending | in_progress | active | suspended | revoked | expired
    metadata: dict = Field(default_factory=dict)
    fides_did: str | None = None
    agit_repo_hash: str | None = None
    created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
    updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))

    class Config:
        json_encoders = {
            datetime: lambda v: v.isoformat(),
            Decimal: lambda v: str(v),
        }

    @staticmethod
    def new(name: str, owner_id: str = "default", **kwargs) -> Agent:
        return Agent(
            agent_id=f"agent_{uuid4().hex[:16]}",
            name=name,
            owner_id=owner_id,
            **kwargs,
        )


class AgentRepository:
    """In-memory agent repository (swap for PostgreSQL in production)."""

    def __init__(self, dsn: str = "memory://"):
        self._dsn = dsn
        self._agents: dict[str, Agent] = {}

    async def create(
        self,
        name: str,
        owner_id: str = "default",
        description: str | None = None,
        spending_limits: SpendingLimits | None = None,
        policy: AgentPolicy | None = None,
        metadata: dict | None = None,
        kya_level: str = "none",
        kya_status: str = "pending",
        fides_did: str | None = None,
        agit_repo_hash: str | None = None,
    ) -> Agent:
        agent = Agent.new(
            name=name,
            owner_id=owner_id,
            description=description,
            spending_limits=spending_limits or SpendingLimits(),
            policy=policy or AgentPolicy(),
            metadata=metadata or {},
            kya_level=kya_level,
            kya_status=kya_status,
            fides_did=fides_did,
            agit_repo_hash=agit_repo_hash,
        )
        self._agents[agent.agent_id] = agent
        return agent

    async def get(self, agent_id: str) -> Agent | None:
        return self._agents.get(agent_id)

    async def list(
        self,
        owner_id: str | None = None,
        is_active: bool | None = None,
        limit: int = 50,
        offset: int = 0,
    ) -> builtins.list[Agent]:
        agents = list(self._agents.values())
        if owner_id:
            agents = [a for a in agents if a.owner_id == owner_id]
        if is_active is not None:
            agents = [a for a in agents if a.is_active == is_active]
        return agents[offset : offset + limit]

    async def update(
        self,
        agent_id: str,
        name: str | None = None,
        description: str | None = None,
        spending_limits: SpendingLimits | None = None,
        policy: AgentPolicy | None = None,
        is_active: bool | None = None,
        metadata: dict | None = None,
        kya_level: str | None = None,
        kya_status: str | None = None,
        fides_did: str | None = None,
        agit_repo_hash: str | None = None,
    ) -> Agent | None:
        agent = self._agents.get(agent_id)
        if not agent:
            return None
        if name is not None:
            agent.name = name
        if description is not None:
            agent.description = description
        if spending_limits is not None:
            agent.spending_limits = spending_limits
        if policy is not None:
            agent.policy = policy
        if is_active is not None:
            agent.is_active = is_active
        if metadata is not None:
            agent.metadata = metadata
        if kya_level is not None:
            agent.kya_level = kya_level
        if kya_status is not None:
            agent.kya_status = kya_status
        if fides_did is not None:
            agent.fides_did = fides_did
        if agit_repo_hash is not None:
            agent.agit_repo_hash = agit_repo_hash
        agent.updated_at = datetime.now(UTC)
        return agent

    async def delete(self, agent_id: str) -> bool:
        if agent_id in self._agents:
            del self._agents[agent_id]
            return True
        return False

    async def bind_wallet(self, agent_id: str, wallet_id: str) -> Agent | None:
        agent = self._agents.get(agent_id)
        if not agent:
            return None
        agent.wallet_id = wallet_id
        agent.updated_at = datetime.now(UTC)
        return agent
