"""
OpenFang Python SDK — helper library for writing Python agents.

Usage:

    from openfang_sdk import Agent

    agent = Agent()

    @agent.on_message
    def handle(message: str, context: dict) -> str:
        return f"You said: {message}"

    agent.run()

Or for simple scripts without the decorator pattern:

    from openfang_sdk import read_input, respond

    data = read_input()
    result = f"Echo: {data['message']}"
    respond(result)
"""

import json
import os
import sys
from typing import Callable, Optional, Dict, Any


def read_input() -> Dict[str, Any]:
    """Read the input JSON from stdin (sent by the OpenFang kernel)."""
    line = sys.stdin.readline().strip()
    if not line:
        # Fallback: check environment variables
        agent_id = os.environ.get("OPENFANG_AGENT_ID", "")
        message = os.environ.get("OPENFANG_MESSAGE", "")
        return {
            "type": "message",
            "agent_id": agent_id,
            "message": message,
            "context": {},
        }
    return json.loads(line)


def respond(text: str, metadata: Optional[Dict[str, Any]] = None) -> None:
    """Send a response back to the OpenFang kernel via stdout."""
    response = {"type": "response", "text": text}
    if metadata:
        response["metadata"] = metadata
    print(json.dumps(response), flush=True)


def log(message: str, level: str = "info") -> None:
    """Log a message to stderr (visible in OpenFang daemon logs)."""
    print(f"[{level.upper()}] {message}", file=sys.stderr, flush=True)


class Agent:
    """Decorator-based Python agent framework.

    Example:

        agent = Agent()

        @agent.on_message
        def handle(message: str, context: dict) -> str:
            return f"Hello! You said: {message}"

        agent.run()
    """

    def __init__(self):
        self._handler: Optional[Callable] = None
        self._setup: Optional[Callable] = None
        self._teardown: Optional[Callable] = None

    def on_message(self, func: Callable) -> Callable:
        """Register a message handler function.

        The function should accept (message: str, context: dict) and return str.
        """
        self._handler = func
        return func

    def on_setup(self, func: Callable) -> Callable:
        """Register a setup function called once before message handling."""
        self._setup = func
        return func

    def on_teardown(self, func: Callable) -> Callable:
        """Register a teardown function called once after message handling."""
        self._teardown = func
        return func

    def run(self) -> None:
        """Run the agent, reading input and producing output."""
        if self._handler is None:
            log("No message handler registered", "error")
            sys.exit(1)

        try:
            if self._setup:
                self._setup()

            data = read_input()
            message = data.get("message", "")
            context = data.get("context", {})

            result = self._handler(message, context)

            if isinstance(result, str):
                respond(result)
            elif isinstance(result, dict):
                respond(result.get("text", str(result)), result.get("metadata"))
            else:
                respond(str(result))

        except Exception as e:
            log(f"Agent error: {e}", "error")
            respond(f"Error: {e}")
            sys.exit(1)
        finally:
            if self._teardown:
                try:
                    self._teardown()
                except Exception as e:
                    log(f"Teardown error: {e}", "error")


# Convenience: if this file is run directly, show usage
if __name__ == "__main__":
    print("OpenFang Python SDK")
    print("====================")
    print()
    print("Import this module in your agent scripts:")
    print()
    print("  from openfang_sdk import Agent")
    print()
    print("  agent = Agent()")
    print()
    print("  @agent.on_message")
    print("  def handle(message, context):")
    print("      return f'You said: {message}'")
    print()
    print("  agent.run()")
