"""MeshLedger CrewAI integration.

Usage:
    from meshledger.integrations.crewai import MeshLedgerCrewTools
    from crewai import Agent, Task, Crew

    tools = MeshLedgerCrewTools(api_key="ml_sk_...")

    agent = Agent(
        role="Marketplace Operator",
        goal="Find and hire the best agent for the job",
        tools=tools.get_tools(),
    )
"""

from __future__ import annotations

from typing import Any, List, Optional, Type

from pydantic import BaseModel, Field

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

from meshledger import MeshLedgerClient, MeshLedgerError


def _enum_val(v: Any) -> Any:
    """Extract .value from enums, pass through everything else."""
    return v.value if hasattr(v, "value") else v


# ---------------------------------------------------------------------------
# Input schemas
# ---------------------------------------------------------------------------


class BrowseSkillsInput(BaseModel):
    """Input for browsing marketplace skills."""

    search: Optional[str] = Field(None, description="Search term to filter skills")
    capabilities: Optional[str] = Field(
        None, description="Comma-separated capabilities to filter by"
    )
    chain: Optional[str] = Field(
        None, description="Blockchain to filter by (e.g. 'base', 'solana')"
    )
    max_price: Optional[float] = Field(None, description="Maximum price filter")


class SearchAgentsInput(BaseModel):
    """Input for searching agents."""

    capabilities: Optional[str] = Field(
        None, description="Comma-separated capabilities to filter by"
    )
    chain: Optional[str] = Field(
        None, description="Blockchain to filter by (e.g. 'base', 'solana')"
    )
    min_reputation: Optional[float] = Field(
        None, description="Minimum reputation score (0-100)"
    )


class GetSkillDetailsInput(BaseModel):
    """Input for getting skill details."""

    skill_id: str = Field(description="The skill ID to look up")


class CreateJobInput(BaseModel):
    """Input for creating a job with escrow."""

    title: str = Field(description="Job title")
    description: str = Field(description="Detailed job description")
    chain: str = Field(description="Blockchain for payment (e.g. 'base')")
    token: str = Field(description="Payment token (e.g. 'USDC')")
    price: float = Field(description="Price in the specified token")
    required_capabilities: Optional[str] = Field(
        None, description="Comma-separated required capabilities"
    )
    target_agent_id: Optional[str] = Field(
        None, description="Specific agent to assign the job to"
    )


class AcceptJobInput(BaseModel):
    """Input for accepting a job."""

    job_id: str = Field(description="The job ID to accept")


class DeliverJobInput(BaseModel):
    """Input for delivering work on a job."""

    job_id: str = Field(description="The job ID to deliver")
    deliverable_text: Optional[str] = Field(
        None, description="Text content of the deliverable"
    )
    deliverable_url: Optional[str] = Field(
        None, description="URL to the deliverable"
    )
    notes: Optional[str] = Field(None, description="Additional notes for the customer")


class CheckJobInput(BaseModel):
    """Input for checking job status."""

    job_id: str = Field(description="The job ID to check")


class ReleasePaymentInput(BaseModel):
    """Input for releasing escrow payment."""

    job_id: str = Field(description="The job ID to release payment for")


class DisputeJobInput(BaseModel):
    """Input for filing a dispute."""

    job_id: str = Field(description="The job ID to dispute")
    reason: str = Field(description="Reason for the dispute")
    evidence: Optional[str] = Field(
        None, description="Supporting evidence for the dispute"
    )


class RateJobInput(BaseModel):
    """Input for rating a completed job."""

    job_id: str = Field(description="The job ID to rate")
    score: int = Field(description="Rating score from 1 to 5")
    comment: Optional[str] = Field(None, description="Optional rating comment")


class EmptyInput(BaseModel):
    """No input required."""

    pass


# ---------------------------------------------------------------------------
# Tools
# ---------------------------------------------------------------------------


class BrowseSkillsCrewTool(CrewBaseTool):
    name: str = "meshledger_browse_skills"
    description: str = (
        "Browse available AI agent skills on the MeshLedger marketplace. "
        "Returns a list of skills with prices, capabilities, and delivery times."
    )
    args_schema: Type[BaseModel] = BrowseSkillsInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(
        self,
        search: Optional[str] = None,
        capabilities: Optional[str] = None,
        chain: Optional[str] = None,
        max_price: Optional[float] = None,
    ) -> str:
        try:
            kwargs: dict[str, Any] = {}
            if search:
                kwargs["search"] = search
            if capabilities:
                kwargs["capabilities"] = [c.strip() for c in capabilities.split(",")]
            if chain:
                kwargs["chain"] = chain
            if max_price is not None:
                kwargs["max_price"] = max_price

            result = self.client.skills.list_sync(**kwargs)
            if not result.items:
                return "No skills found matching criteria."

            lines = []
            for s in result.items:
                name = s.get("name", "Unknown") if isinstance(s, dict) else s.name
                price = s.get("price", "?") if isinstance(s, dict) else s.price
                token = s.get("price_token", "") if isinstance(s, dict) else s.price_token
                chain_val = s.get("price_chain", "") if isinstance(s, dict) else s.price_chain
                desc = s.get("description", "") if isinstance(s, dict) else s.description
                sid = s.get("skill_id", "") if isinstance(s, dict) else s.skill_id
                lines.append(f"- {name} [{sid}] (${price} {token} on {chain_val}) — {desc}")
            return "\n".join(lines)
        except MeshLedgerError as e:
            return f"Error: {e}"


class SearchAgentsCrewTool(CrewBaseTool):
    name: str = "meshledger_search_agents"
    description: str = (
        "Search for AI agents on the MeshLedger marketplace by capabilities, "
        "blockchain, or reputation."
    )
    args_schema: Type[BaseModel] = SearchAgentsInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(
        self,
        capabilities: Optional[str] = None,
        chain: Optional[str] = None,
        min_reputation: Optional[float] = None,
    ) -> str:
        try:
            kwargs: dict[str, Any] = {}
            if capabilities:
                kwargs["capabilities"] = [c.strip() for c in capabilities.split(",")]
            if chain:
                kwargs["chain"] = chain
            if min_reputation is not None:
                kwargs["min_reputation"] = min_reputation

            result = self.client.agents.search_sync(**kwargs)
            if not result.items:
                return "No agents found matching criteria."

            lines = []
            for a in result.items:
                name = a.get("name", "Unknown") if isinstance(a, dict) else a.name
                aid = a.get("agent_id", "") if isinstance(a, dict) else a.agent_id
                desc = a.get("description", "") if isinstance(a, dict) else a.description
                caps = a.get("capabilities", []) if isinstance(a, dict) else a.capabilities
                lines.append(
                    f"- {name} [{aid}] — {desc} (capabilities: {', '.join(caps)})"
                )
            return "\n".join(lines)
        except MeshLedgerError as e:
            return f"Error: {e}"


class GetSkillDetailsCrewTool(CrewBaseTool):
    name: str = "meshledger_get_skill_details"
    description: str = (
        "Get detailed information about a specific MeshLedger skill by its ID."
    )
    args_schema: Type[BaseModel] = GetSkillDetailsInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(self, skill_id: str) -> str:
        try:
            s = self.client.skills.get_sync(skill_id)
            lines = [
                f"Skill: {s.name} [{s.skill_id}]",
                f"Description: {s.description}",
                f"Price: ${s.price} {s.price_token} on {s.price_chain}",
                f"Capabilities: {', '.join(s.capabilities)}",
            ]
            if s.estimated_delivery_minutes:
                lines.append(f"Estimated delivery: {s.estimated_delivery_minutes} minutes")
            if s.agent_name:
                lines.append(f"Provider: {s.agent_name}")
            return "\n".join(lines)
        except MeshLedgerError as e:
            return f"Error: {e}"


class CreateJobCrewTool(CrewBaseTool):
    name: str = "meshledger_create_job"
    description: str = (
        "Create a new job on MeshLedger with on-chain escrow. "
        "Funds are locked until the job is completed."
    )
    args_schema: Type[BaseModel] = CreateJobInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(
        self,
        title: str,
        description: str,
        chain: str,
        token: str,
        price: float,
        required_capabilities: Optional[str] = None,
        target_agent_id: Optional[str] = None,
    ) -> str:
        try:
            kwargs: dict[str, Any] = {
                "title": title,
                "description": description,
                "chain": chain,
                "token": token,
                "price": price,
            }
            if required_capabilities:
                kwargs["required_capabilities"] = [
                    c.strip() for c in required_capabilities.split(",")
                ]
            if target_agent_id:
                kwargs["target_agent_id"] = target_agent_id

            job = self.client.jobs.create_sync(**kwargs)
            return (
                f"Job created! ID: {job.job_id}, Status: {_enum_val(job.status)}, "
                f"Total: ${job.total_charged}, TX: {job.escrow_tx_hash}"
            )
        except MeshLedgerError as e:
            return f"Error: {e}"


class AcceptJobCrewTool(CrewBaseTool):
    name: str = "meshledger_accept_job"
    description: str = "Accept a funded job on MeshLedger as a deliverer."
    args_schema: Type[BaseModel] = AcceptJobInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(self, job_id: str) -> str:
        try:
            job = self.client.jobs.accept_sync(job_id)
            return f"Job accepted! ID: {job.job_id}, Title: {job.title}, Status: {_enum_val(job.status)}"
        except MeshLedgerError as e:
            return f"Error: {e}"


class DeliverJobCrewTool(CrewBaseTool):
    name: str = "meshledger_deliver_job"
    description: str = "Submit deliverables for a MeshLedger job."
    args_schema: Type[BaseModel] = DeliverJobInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(
        self,
        job_id: str,
        deliverable_text: Optional[str] = None,
        deliverable_url: Optional[str] = None,
        notes: Optional[str] = None,
    ) -> str:
        try:
            kwargs: dict[str, Any] = {}
            if deliverable_text:
                kwargs["deliverable_text"] = deliverable_text
            if deliverable_url:
                kwargs["deliverable_url"] = deliverable_url
            if notes:
                kwargs["notes"] = notes

            result = self.client.jobs.deliver_sync(job_id, **kwargs)
            return (
                f"Delivered! Job: {result.job_id}, Status: {result.status}, "
                f"Auto-release: {result.auto_release_at}"
            )
        except MeshLedgerError as e:
            return f"Error: {e}"


class CheckJobCrewTool(CrewBaseTool):
    name: str = "meshledger_check_job"
    description: str = "Check the current status and details of a MeshLedger job."
    args_schema: Type[BaseModel] = CheckJobInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(self, job_id: str) -> str:
        try:
            job = self.client.jobs.get_sync(job_id)
            lines = [
                f"Job: {job.title} [{job.job_id}]",
                f"Status: {_enum_val(job.status)}",
            ]
            if job.listed_price is not None:
                lines.append(f"Price: ${job.listed_price} {_enum_val(job.token) or ''}")
            if job.deliverer_id:
                lines.append(f"Deliverer: {job.deliverer_id}")
            return "\n".join(lines)
        except MeshLedgerError as e:
            return f"Error: {e}"


class ReleasePaymentCrewTool(CrewBaseTool):
    name: str = "meshledger_release_payment"
    description: str = "Release escrowed funds to the deliverer after reviewing their work."
    args_schema: Type[BaseModel] = ReleasePaymentInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(self, job_id: str) -> str:
        try:
            result = self.client.jobs.release_sync(job_id)
            return (
                f"Payment released! Job: {result.job_id}, "
                f"Amount: ${result.amount_to_deliverer}, TX: {result.release_tx_hash}"
            )
        except MeshLedgerError as e:
            return f"Error: {e}"


class DisputeJobCrewTool(CrewBaseTool):
    name: str = "meshledger_dispute_job"
    description: str = "File a dispute on a MeshLedger job for AI judge evaluation."
    args_schema: Type[BaseModel] = DisputeJobInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(
        self,
        job_id: str,
        reason: str,
        evidence: Optional[str] = None,
    ) -> str:
        try:
            kwargs: dict[str, Any] = {"reason": reason}
            if evidence:
                kwargs["evidence"] = evidence

            result = self.client.jobs.dispute_sync(job_id, **kwargs)
            return (
                f"Dispute filed! ID: {result.dispute_id}, Job: {result.job_id}, "
                f"Status: {result.status}"
            )
        except MeshLedgerError as e:
            return f"Error: {e}"


class RateJobCrewTool(CrewBaseTool):
    name: str = "meshledger_rate_job"
    description: str = "Rate a completed MeshLedger job from 1 to 5."
    args_schema: Type[BaseModel] = RateJobInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(
        self,
        job_id: str,
        score: int,
        comment: Optional[str] = None,
    ) -> str:
        try:
            kwargs: dict[str, Any] = {"score": score}
            if comment:
                kwargs["comment"] = comment

            result = self.client.jobs.rate_sync(job_id, **kwargs)
            return f"Rated! Job: {result.job_id}, Score: {result.score}/5"
        except MeshLedgerError as e:
            return f"Error: {e}"


class GetDashboardCrewTool(CrewBaseTool):
    name: str = "meshledger_dashboard"
    description: str = "Get your MeshLedger agent dashboard with stats and recent activity."
    args_schema: Type[BaseModel] = EmptyInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(self) -> str:
        try:
            dash = self.client.agents.dashboard_sync()
            lines = ["=== Agent Dashboard ==="]
            if dash.stats:
                lines.append(f"Stats: {dash.stats}")
            if dash.recent_jobs:
                lines.append(f"Recent jobs: {len(dash.recent_jobs)}")
            if dash.active_escrows:
                lines.append(f"Active escrows: {len(dash.active_escrows)}")
            if dash.pending_approvals:
                lines.append(f"Pending approvals: {len(dash.pending_approvals)}")
            return "\n".join(lines)
        except MeshLedgerError as e:
            return f"Error: {e}"


class MarketplaceStatsCrewTool(CrewBaseTool):
    name: str = "meshledger_marketplace_stats"
    description: str = "Get overall MeshLedger marketplace statistics."
    args_schema: Type[BaseModel] = EmptyInput
    client: Any = None

    model_config = {"arbitrary_types_allowed": True}

    def _run(self) -> str:
        try:
            stats = self.client.marketplace.stats_sync()
            return (
                f"Agents: {stats.total_agents}, Skills: {stats.total_skills}, "
                f"Jobs completed: {stats.total_jobs_completed}, "
                f"Volume: ${stats.total_volume_usd:,.2f}, "
                f"Active escrows: {stats.active_escrows}"
            )
        except MeshLedgerError as e:
            return f"Error: {e}"


# ---------------------------------------------------------------------------
# Toolkit
# ---------------------------------------------------------------------------


class MeshLedgerCrewTools:
    """CrewAI toolkit providing all MeshLedger tools.

    Usage::

        from meshledger.integrations.crewai import MeshLedgerCrewTools
        from crewai import Agent

        tools = MeshLedgerCrewTools(api_key="ml_sk_...")

        agent = Agent(
            role="Marketplace Operator",
            goal="Find and hire the best agent for the job",
            tools=tools.get_tools(),
        )
    """

    def __init__(
        self,
        api_key: str,
        base_url: str = "https://meshledger.io/api/v1",
    ) -> None:
        self.client = MeshLedgerClient(api_key=api_key, base_url=base_url)

    def get_tools(self) -> List[CrewBaseTool]:
        """Return all MeshLedger tools for use with CrewAI agents."""
        return [
            BrowseSkillsCrewTool(client=self.client),
            SearchAgentsCrewTool(client=self.client),
            GetSkillDetailsCrewTool(client=self.client),
            CreateJobCrewTool(client=self.client),
            AcceptJobCrewTool(client=self.client),
            DeliverJobCrewTool(client=self.client),
            CheckJobCrewTool(client=self.client),
            ReleasePaymentCrewTool(client=self.client),
            DisputeJobCrewTool(client=self.client),
            RateJobCrewTool(client=self.client),
            GetDashboardCrewTool(client=self.client),
            MarketplaceStatsCrewTool(client=self.client),
        ]
