"""Vorim SDK — CrewAI Python integration.

Provides crew registration, permission-checked tool execution,
task audit logging, and delegation control for CrewAI agents.

Requires: ``pip install vorim[crewai]``

Usage::

    from vorim import Vorim
    from vorim.integrations.crewai import register_crew, vorim_tool

    vorim = Vorim(api_key="agid_sk_live_...")

    crew = register_crew(vorim, {
        "crew_name": "content-pipeline",
        "members": [
            {
                "role": "researcher",
                "name": "crew-researcher",
                "capabilities": ["web_search"],
                "scopes": ["agent:read", "agent:execute"],
            },
        ],
    })
"""

from __future__ import annotations

import time
from dataclasses import dataclass, field
from functools import wraps
from typing import Any, Callable

try:
    from crewai.tools import BaseTool as CrewAIBaseTool
except ImportError as e:
    raise ImportError(
        "CrewAI integration requires crewai. "
        "Install with: pip install vorim[crewai]"
    ) from e

from ..client import Vorim
from ..types import AuditEventInput, PermissionScope


# ── Crew Registration ────────────────────────────────────────────────────


@dataclass
class CrewMemberConfig:
    """Configuration for a single crew member."""

    role: str
    name: str
    capabilities: list[str]
    scopes: list[PermissionScope]
    description: str | None = None
    allow_delegation: bool = False


@dataclass
class RegisteredCrewMember:
    """A crew member that has been registered with Vorim."""

    role: str
    agent_id: str
    private_key: str
    registration: Any


@dataclass
class RegisteredCrew:
    """A fully registered CrewAI crew with Vorim identities."""

    crew_name: str
    members: list[RegisteredCrewMember]

    def get_member(self, role: str) -> RegisteredCrewMember | None:
        """Lookup a member by role."""
        return next((m for m in self.members if m.role == role), None)

    def agent_ids(self) -> list[str]:
        """Get all agent IDs in the crew."""
        return [m.agent_id for m in self.members]


def register_crew(
    vorim: Vorim,
    manifest: dict[str, Any],
) -> RegisteredCrew:
    """Register an entire CrewAI crew with Vorim.

    Each crew member gets a unique Vorim agent identity with Ed25519
    keypair and scoped permissions.

    Args:
        vorim: Vorim client instance.
        manifest: Dict with ``crew_name`` and ``members`` list. Each member
            needs ``role``, ``name``, ``capabilities``, ``scopes``, and
            optionally ``description`` and ``allow_delegation``.

    Usage::

        crew = register_crew(vorim, {
            "crew_name": "content-pipeline",
            "members": [
                {
                    "role": "researcher",
                    "name": "crew-researcher",
                    "capabilities": ["web_search", "summarization"],
                    "scopes": ["agent:read", "agent:execute"],
                },
                {
                    "role": "writer",
                    "name": "crew-writer",
                    "capabilities": ["file_write"],
                    "scopes": ["agent:read", "agent:write"],
                    "allow_delegation": True,
                },
            ],
        })

        researcher = crew.get_member("researcher")
        print(researcher.agent_id)
    """
    crew_name = manifest["crew_name"]
    registered: list[RegisteredCrewMember] = []

    for member in manifest["members"]:
        scopes = list(member["scopes"])

        if member.get("allow_delegation") and "agent:delegate" not in scopes:
            scopes.append("agent:delegate")

        reg = vorim.register(
            name=member["name"],
            description=member.get("description", f"CrewAI {member['role']} — {crew_name}"),
            capabilities=member["capabilities"],
            scopes=scopes,
        )

        registered.append(
            RegisteredCrewMember(
                role=member["role"],
                agent_id=reg.agent.agent_id,
                private_key=reg.private_key,
                registration=reg,
            )
        )

    return RegisteredCrew(crew_name=crew_name, members=registered)


# ── Tool Decorator ───────────────────────────────────────────────────────


def vorim_tool(
    vorim: Vorim,
    agent_id: str,
    permission: PermissionScope = "agent:execute",
) -> Callable:
    """Decorator that wraps a function with Vorim permission checks and
    audit logging, returning a CrewAI-compatible tool.

    Usage::

        @vorim_tool(vorim, agent_id="agid_acme_...", permission="agent:read")
        def search_docs(query: str) -> str:
            \"\"\"Search internal documents.\"\"\"
            return do_search(query)
    """

    def decorator(func: Callable) -> Callable:
        tool_name = func.__name__

        @wraps(func)
        def guarded(*args: Any, **kwargs: Any) -> Any:
            result = vorim.check(agent_id, permission)
            if not result.allowed:
                vorim.emit(
                    agent_id=agent_id,
                    event_type="tool_call",
                    action=tool_name,
                    result="denied",
                    permission=permission,
                    metadata={"reason": result.reason, "framework": "crewai"},
                )
                raise PermissionError(
                    f"Vorim: permission denied for '{tool_name}' — "
                    f"scope '{permission}'"
                    f"{f': {result.reason}' if result.reason else ''}"
                )

            start = time.monotonic()
            try:
                output = func(*args, **kwargs)
            except Exception as exc:
                vorim.emit(
                    agent_id=agent_id,
                    event_type="tool_call",
                    action=tool_name,
                    result="error",
                    permission=permission,
                    latency_ms=int((time.monotonic() - start) * 1000),
                    error_code=type(exc).__name__,
                    metadata={"error": str(exc), "framework": "crewai"},
                )
                raise

            vorim.emit(
                agent_id=agent_id,
                event_type="tool_call",
                action=tool_name,
                result="success",
                permission=permission,
                latency_ms=int((time.monotonic() - start) * 1000),
                metadata={"framework": "crewai"},
            )
            return output

        return guarded

    return decorator


# ── Task Audit ───────────────────────────────────────────────────────────


def emit_crew_task_event(
    vorim: Vorim,
    *,
    role: str,
    agent_id: str,
    task: str,
    result: str,
    tool: str | None = None,
    latency_ms: int | None = None,
    error: str | None = None,
    delegated_to: str | None = None,
    metadata: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Emit a Vorim audit event for a CrewAI task execution.

    Usage::

        emit_crew_task_event(
            vorim,
            role="researcher",
            agent_id=crew.get_member("researcher").agent_id,
            task="research_competitors",
            tool="web_search",
            result="success",
            latency_ms=3200,
        )
    """
    meta: dict[str, Any] = {"framework": "crewai", "role": role}
    if delegated_to:
        meta["delegated_to"] = delegated_to
    if error:
        meta["error"] = error
    if metadata:
        meta.update(metadata)

    return vorim.emit(
        agent_id=agent_id,
        event_type="tool_call" if tool else "api_request",
        action=task,
        resource=tool,
        result=result,
        latency_ms=latency_ms,
        error_code="CREW_TASK_ERROR" if error else None,
        metadata=meta,
    )


def emit_crew_run_events(
    vorim: Vorim,
    events: list[dict[str, Any]],
) -> dict[str, Any]:
    """Emit audit events for an entire crew run (batch).

    Each event dict should have: ``role``, ``agent_id``, ``task``, ``result``,
    and optionally ``tool``, ``latency_ms``, ``error``, ``delegated_to``, ``metadata``.
    """
    audit_events = []
    for ev in events:
        meta: dict[str, Any] = {"framework": "crewai", "role": ev["role"]}
        if ev.get("delegated_to"):
            meta["delegated_to"] = ev["delegated_to"]
        if ev.get("error"):
            meta["error"] = ev["error"]
        if ev.get("metadata"):
            meta.update(ev["metadata"])

        audit_events.append(
            AuditEventInput(
                agent_id=ev["agent_id"],
                event_type="tool_call" if ev.get("tool") else "api_request",
                action=ev["task"],
                resource=ev.get("tool"),
                result=ev["result"],
                latency_ms=ev.get("latency_ms"),
                error_code="CREW_TASK_ERROR" if ev.get("error") else None,
                metadata=meta,
            )
        )

    return vorim.emit_batch(audit_events)


# ── Permission Helpers ───────────────────────────────────────────────────


def check_crew_permission(
    vorim: Vorim,
    crew: RegisteredCrew,
    role: str,
    scope: PermissionScope,
) -> dict[str, Any]:
    """Check if a crew member has a given permission scope."""
    member = crew.get_member(role)
    if not member:
        return {"allowed": False, "reason": f"Unknown crew member: {role}"}
    result = vorim.check(member.agent_id, scope)
    return {"allowed": result.allowed, "reason": result.reason}


def check_delegation_permission(
    vorim: Vorim,
    crew: RegisteredCrew,
    from_role: str,
    to_role: str,
) -> dict[str, Any]:
    """Check if delegation between two crew members is permitted."""
    from_member = crew.get_member(from_role)
    to_member = crew.get_member(to_role)
    if not from_member:
        return {"allowed": False, "reason": f"Unknown crew member: {from_role}"}
    if not to_member:
        return {"allowed": False, "reason": f"Unknown crew member: {to_role}"}

    result = vorim.check(from_member.agent_id, "agent:delegate")
    if not result.allowed:
        return {"allowed": False, "reason": f"{from_role} lacks agent:delegate permission"}
    return {"allowed": True}


# ── Trust Verification ───────────────────────────────────────────────────


def verify_crew_trust(
    vorim: Vorim,
    crew: RegisteredCrew,
) -> list[dict[str, Any]]:
    """Verify trust scores for all members of a crew."""
    results = []
    for member in crew.members:
        trust = vorim.verify(member.agent_id)
        results.append({
            "role": member.role,
            "agent_id": member.agent_id,
            "trust_score": trust.trust_score,
            "status": trust.status,
        })
    return results
