"""Protocol contracts for all bridges and providers.

All new contracts use typing.Protocol (PEP 544) for structural subtyping.
Adapters satisfy a protocol by implementing the right methods -- no inheritance
required. This makes everything swappable via the loader.py registry.

Bridge protocols define how external content reaches agents.
Provider protocols define how data is fetched from or generated by external services.

Key rule: Bridges NEVER assume a provider feeds them. Content can come from
users, SMS, email, API, or file drops.
"""

from __future__ import annotations

from typing import Protocol, runtime_checkable

# ---------------------------------------------------------------------------
# Bridge Protocols — how content reaches agents
# ---------------------------------------------------------------------------


@runtime_checkable
class CodingAgentBridge(Protocol):
    """Contract for coding agent integrations (Cursor, Copilot, Claude Code, Windsurf).

    A coding agent bridge is the channel through which a coding assistant
    (like Cursor) communicates with Vastian agents — querying them,
    running quizzes, presenting plans, and persisting the results.
    """

    async def query_agent(self, agent_id: str, message: str) -> str:
        """Send a message to a single agent and return the response."""
        ...

    async def query_agents(self, agent_ids: list[str], message: str) -> dict[str, str]:
        """Send the same message to multiple agents in parallel."""
        ...

    async def quiz_agents(self, agent_ids: list[str], questions: str) -> dict[str, str]:
        """Quiz multiple agents with structured questions."""
        ...

    async def present(self, agent_ids: list[str], brief: str, topic: str = "") -> dict:
        """Present a brief to agents and collect responses."""
        ...

    def persist_briefs(self, results: dict[str, str], topic: str) -> list[str]:
        """Write results to docs/{agent_id}/briefs/ and return paths."""
        ...


@runtime_checkable
class MemoryBridge(Protocol):
    """Contract for external memory/context systems (Giga, or a custom replacement).

    A memory bridge writes plans and knowledge units to an external memory
    system so that all coding agents and Vastian agents share context.
    """

    def write_plan(
        self,
        agent_id: str,
        key_name: str,
        content: str,
        mind: str = "vastian-network",
    ) -> str:
        """Write a plan to the memory system. Returns confirmation."""
        ...

    def write_neuron(
        self,
        agent_id: str,
        title: str,
        body: str,
        mind: str = "vastian-network",
    ) -> str:
        """Write a knowledge unit to the memory system. Returns confirmation."""
        ...

    # -- Inbox (pull) methods -- added in Mercury Wave 2F -------------------

    def pull_plans(self, mind: str) -> list[dict]:
        """Pull plans from external memory for the given mind.

        Returns a list of plan dicts with at least {key_name, content, pulled_at}.
        """
        ...

    def pull_neurons(self, mind: str, task: str = "") -> list[dict]:
        """Pull neurons from external memory for the given mind.

        Args:
            mind: Target mind/namespace.
            task: Optional relevance hint for the external memory to filter by.

        Returns a list of neuron dicts with at least {title, body, pulled_at}.
        """
        ...

    def sync_inbox(self, mind: str) -> int:
        """Pull plans + neurons and write them to the inbox directory.

        Combines pull_plans and pull_neurons, writes JSON files to
        state/memory-inbox/{mind}/plans/ and state/memory-inbox/{mind}/neurons/.

        Returns the count of new items written.
        """
        ...


@runtime_checkable
class ContentBridge(Protocol):
    """Contract for routing external content to agents.

    Content can come from ANY source: manual input, meeting summaries,
    transcripts, SMS via Zo, email, file drops. The bridge does not
    care about the source — it routes content to the right agents.
    """

    async def ingest(self, content: str, metadata: dict) -> dict:
        """Process content and route to target agents. Returns results."""
        ...

    def get_target_agents(self, content: str, metadata: dict) -> list[str]:
        """Determine which agents should process this content."""
        ...

    def persist_outcomes(self, results: dict) -> None:
        """Write processing results to docs/state/outbox."""
        ...


# ---------------------------------------------------------------------------
# Provider Protocols — how data is fetched/generated
# ---------------------------------------------------------------------------


@runtime_checkable
class LLMProvider(Protocol):
    """Contract for LLM integrations (OpenAI, Anthropic, local models).

    Migrated from abc.ABC in Phase Mercury. Existing plugins (openai_llm,
    anthropic_llm) already implement these methods — no changes needed.
    """

    async def generate(
        self,
        system_prompt: str,
        messages: list[dict[str, str]],
        model: str | None = None,
        temperature: float = 0.7,
        max_tokens: int = 4096,
    ) -> str:
        """Generate a text response from the LLM."""
        ...

    async def generate_with_tools(
        self,
        system_prompt: str,
        messages: list[dict[str, str]],
        tools: list[dict],
        model: str | None = None,
        temperature: float = 0.7,
    ) -> dict:
        """Generate a response that may include tool calls."""
        ...


@runtime_checkable
class TranscriptProvider(Protocol):
    """Contract for fetching meeting transcripts (Pocket API, Teams manual paste).

    A transcript provider retrieves raw transcript text — either from an API
    (like Pocket) or from manual input (like pasting a Teams transcript).
    """

    async def fetch_transcript(self, recording_id: str) -> dict:
        """Fetch a transcript by recording ID. Returns {text, speakers, metadata}."""
        ...

    async def list_recordings(
        self,
        start_date: str | None = None,
        end_date: str | None = None,
    ) -> list[dict]:
        """List available recordings, optionally filtered by date range."""
        ...


@runtime_checkable
class SummaryProvider(Protocol):
    """Contract for generating meeting summaries (Pocket AI, Vastian analyzer).

    A summary provider generates structured summaries from transcripts.
    The user chooses which provider to use — Pocket's own AI summary or
    Vastian's meeting analyzer (ported from v0agent-main).
    """

    async def generate_summary(self, transcript: str, template: str | None = None) -> dict:
        """Generate a structured summary from transcript text.

        Returns {summary, signals, action_items, metadata}.
        """
        ...

    async def extract_action_items(self, text: str) -> list[dict]:
        """Extract action items from a transcript or summary."""
        ...


@runtime_checkable
class FinanceProvider(Protocol):
    """Contract for banking integrations (Mercury, or other bank APIs)."""

    async def get_balances(self) -> list[dict]:
        """Get balances for all configured accounts."""
        ...

    async def list_accounts(self) -> list[dict]:
        """List all configured accounts with metadata."""
        ...

    async def get_transactions(
        self,
        account_id: str,
        start_date: str | None = None,
        end_date: str | None = None,
    ) -> list[dict]:
        """Get transactions for an account, optionally filtered by date."""
        ...


@runtime_checkable
class StorageProvider(Protocol):
    """Contract for cloud storage integrations (Google Drive, Dropbox, Box, Tresorit).

    Part of the local-first backup system (Mercury Wave 3). User data never
    lives on Vastian servers — backups go to the user's own cloud storage.
    Tresorit is the zero-knowledge encrypted option for privacy-first users.
    Each adapter handles OAuth2 auth and token storage in config/local/.
    """

    async def upload(self, local_path: str, remote_path: str) -> dict:
        """Upload a local file to the remote storage.

        Returns metadata dict with at least {remote_path, size, timestamp}.
        """
        ...

    async def download(self, remote_path: str, local_path: str) -> dict:
        """Download a file from remote storage to a local path.

        Returns metadata dict with at least {local_path, size, timestamp}.
        """
        ...

    async def list_files(self, prefix: str = "") -> list[dict]:
        """List files in remote storage, optionally filtered by prefix.

        Returns list of dicts with at least {remote_path, size, modified}.
        """
        ...

    async def delete(self, remote_path: str) -> bool:
        """Delete a file from remote storage. Returns True on success."""
        ...

    def get_auth_url(self) -> str:
        """Get the OAuth2 authorization URL for the user to authenticate."""
        ...

    def is_authenticated(self) -> bool:
        """Check if the provider has valid credentials."""
        ...


__all__ = [
    "CodingAgentBridge",
    "MemoryBridge",
    "ContentBridge",
    "LLMProvider",
    "TranscriptProvider",
    "SummaryProvider",
    "FinanceProvider",
    "StorageProvider",
]
