#!/usr/bin/env python3
"""Styrene Device Agent - Headless RPC Server

This script runs as a system service on remote devices to handle RPC commands
from authorized operators via LXMF over Reticulum.

Usage:
    ./styrene-device-agent.py [--config PATH]

Configuration:
    Default config: ~/.config/styrene-bond-rpc/auth.yaml
    Override with: --config /path/to/auth.yaml

Installation:
    1. Install dependencies: pip install -r requirements.txt
    2. Configure authorization: cp auth.yaml ~/.config/styrene-bond-rpc/
    3. Edit auth.yaml with authorized operator identity hashes
    4. Run agent: ./styrene-device-agent.py
    5. For systemd service: cp styrene-agent.service /etc/systemd/system/
"""

import asyncio
import logging
import signal
import sys
from pathlib import Path
from argparse import ArgumentParser

# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "packages" / "styrene-bond-rpc" / "src"))

from styrene.services.reticulum import initialize_reticulum, get_operator_identity_object
from styrene.services.lxmf_service import get_lxmf_service
from styrene_bond_rpc.server import RPCServer
from styrene_bond_rpc.auth import AuthorizationService
from styrene_bond_rpc.handlers import (
    handle_status,
    handle_exec,
    handle_reboot,
    handle_update_config,
)


# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
    handlers=[
        logging.FileHandler('/var/log/styrene-agent/agent.log'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger('styrene.agent')


class DeviceAgent:
    """Styrene device agent service."""

    def __init__(self, config_path: Path):
        """Initialize device agent.

        Args:
            config_path: Path to authorization config file
        """
        self.config_path = config_path
        self.rpc_server = None
        self.lxmf_service = None
        self.auth_service = None
        self.running = False

    async def start(self):
        """Start the device agent."""
        logger.info("Starting Styrene Device Agent...")

        # Initialize Reticulum in headless mode
        logger.info("Initializing Reticulum network stack...")
        initialize_reticulum(headless=True)
        identity = get_operator_identity_object()
        logger.info(f"Device identity: {identity.hexhash}")

        # Initialize LXMF service
        logger.info("Initializing LXMF messaging service...")
        self.lxmf_service = get_lxmf_service()
        self.lxmf_service.initialize(identity)

        # Create authorization service
        logger.info(f"Loading authorization config from: {self.config_path}")
        if not self.config_path.exists():
            logger.error(f"Authorization config not found: {self.config_path}")
            logger.error("Create config file with authorized operator identities")
            sys.exit(1)

        self.auth_service = AuthorizationService(str(self.config_path))
        logger.info(f"Loaded {len(self.auth_service._config.get('identities', []))} authorized identities")

        # Create RPC server
        logger.info("Initializing RPC server...")
        self.rpc_server = RPCServer(
            router=self.lxmf_service.router,
            identity=identity,
        )

        # Wrap handlers with authorization
        def authorized_handler(command_type, handler):
            """Wrap handler with authorization check."""
            async def wrapper(msg):
                source_hash = msg.source_hash

                # Check authorization
                if not self.auth_service.is_authorized(source_hash, command_type):
                    logger.warning(f"Unauthorized {command_type} request from {source_hash}")
                    return {
                        "type": "error",
                        "error": f"Unauthorized: {command_type} requires permission",
                        "exit_code": -1,
                    }

                logger.info(f"Authorized {command_type} request from {source_hash}")
                return await handler(msg)

            return wrapper

        # Register authorized handlers
        self.rpc_server.register_handler(
            "status_request",
            authorized_handler("status_request", handle_status)
        )
        self.rpc_server.register_handler(
            "exec",
            authorized_handler("exec", handle_exec)
        )
        self.rpc_server.register_handler(
            "reboot",
            authorized_handler("reboot", handle_reboot)
        )
        self.rpc_server.register_handler(
            "update_config",
            authorized_handler("update_config", handle_update_config)
        )

        # Register message callback
        self.lxmf_service.register_callback(self.rpc_server.handle_message)

        logger.info("Device agent running and ready to receive RPC commands")
        logger.info(f"Listening for messages to: {identity.hexhash}")

        # Run event loop
        self.running = True
        try:
            while self.running:
                await asyncio.sleep(1)
        except KeyboardInterrupt:
            logger.info("Received interrupt signal")
        finally:
            await self.stop()

    async def stop(self):
        """Stop the device agent."""
        logger.info("Stopping Styrene Device Agent...")
        self.running = False
        # Cleanup if needed
        logger.info("Device agent stopped")

    def handle_signal(self, signum, frame):
        """Handle OS signals."""
        logger.info(f"Received signal {signum}")
        self.running = False


def main():
    """Main entry point."""
    parser = ArgumentParser(description="Styrene Device Agent")
    parser.add_argument(
        "--config",
        type=Path,
        default=Path.home() / ".config" / "styrene-bond-rpc" / "auth.yaml",
        help="Path to authorization config file (default: ~/.config/styrene-bond-rpc/auth.yaml)"
    )
    args = parser.parse_args()

    # Create agent
    agent = DeviceAgent(args.config)

    # Register signal handlers
    signal.signal(signal.SIGINT, agent.handle_signal)
    signal.signal(signal.SIGTERM, agent.handle_signal)

    # Run agent
    try:
        asyncio.run(agent.start())
    except Exception as e:
        logger.error(f"Agent crashed: {e}", exc_info=True)
        sys.exit(1)


if __name__ == "__main__":
    main()
