#!/usr/bin/env python3
"""
Trinity OpenAI Adapter with LockStock Chain Authentication (v2 - 16 Capabilities)

This adapter is the trust boundary - all external requests come through here.
The agent_id comes from LOCKSTOCK_AGENT_ID env var (set by operator after
provisioning from the dashboard).

Phase 3 Features:
- Startup resync: Compare local chain state against server authority
- Periodic health check: Detect chain drift every 5 minutes
- Enhanced status endpoint: Shows both local and server state

v2 Expansion (2026-02-12):
- Expanded from 2 to 16 stamped endpoints
- Task strings byte-verified against agent_registry
- All handlers are stubs (chain stamp only, no execution)
"""
import os
import sys
import asyncio
import httpx
import time
import uuid
import logging
from typing import List, Optional, Dict, Any
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from starlette.concurrency import run_in_threadpool
from pydantic import BaseModel

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("trinity-adapter")

# --- Configuration ---
TRINITY_API = os.environ.get("TRINITY_API", "http://localhost:8000")

# --- Agent ID Validation ---
# LOCKSTOCK_AGENT_ID is the source of truth. No hardcoded default.
AGENT_ID = os.environ.get("LOCKSTOCK_AGENT_ID")
if not AGENT_ID:
    logger.error("LOCKSTOCK_AGENT_ID not set. Cannot start adapter without an agent.")
    logger.error("Provision an agent from the LockStock dashboard, then set LOCKSTOCK_AGENT_ID.")
    sys.exit(1)

SOCKET_PATH = os.environ.get("LOCKSTOCK_SOCKET", "/var/run/liberty/liberty.sock")
SERVER_URL = os.environ.get("LOCKSTOCK_SERVER_URL", "https://lockstock-api-i9kp.onrender.com")
HEALTH_CHECK_INTERVAL = int(os.environ.get("LOCKSTOCK_HEALTH_CHECK_INTERVAL", "300"))  # 5 minutes

# --- Path to Task Mapping ---
# Maps URL path prefixes to chain task types
# None means exclude from chain (no audit entry created)
#
# IMPORTANT: Task strings are byte-verified against Postgres agent_registry.
# They are UPPERCASE with NO UNDERSCORES. Do not transform these values.
PATH_TASKS = {
    # AI endpoints
    "/v1/chat/completions":          "CHATCOMPLETION",
    "/v1/chat/completions/response": "CHATCOMPLETIONRESPONSE",
    "/v1/models":                    "NETWORK",
    # Lifecycle
    "/v1/heartbeat":                 "HEARTBEAT",
    "/v1/checkpoint":                "CHECKPOINT",
    "/v1/teleport":                  "TELEPORT",
    # Ops
    "/v1/ops/deploy":                "DEPLOY",
    "/v1/ops/restart":               "RESTART",
    "/v1/ops/backup":                "BACKUP",
    "/v1/ops/rollback":              "ROLLBACK",
    # Tools
    "/v1/tools/file/read":           "FILEREAD",
    "/v1/tools/file/write":          "FILEWRITE",
    "/v1/tools/shell":               "SHELL",
    "/v1/tools/database":            "DATABASE",
    # Agent-to-Agent
    "/v1/a2a/handoff":               "HANDOFF",
    "/v1/a2a/delegate":              "DELEGATE",
    # Unstamped (infrastructure only)
    "/health":                        None,
    "/lockstock/status":              None,
}

# --- Global State ---
_startup_time: float = 0
_requests_since_boot: int = 0
_last_verify_result: str = "none"
_last_server_state: Optional[Dict[str, Any]] = None
_sync_status: str = "UNKNOWN"
_health_check_task: Optional[asyncio.Task] = None


# =============================================================================
# STARTUP RESYNC (Phase 3a)
# =============================================================================

async def fetch_server_state(agent_id: str, api_url: str) -> Optional[Dict[str, Any]]:
    """
    Fetch current chain state from LockStock server.

    Returns:
        Dict with agent_id, last_hash, last_sequence, last_server_timestamp,
        source, state_matrix: {a11, a12, a21, a22}

        Returns None if server unreachable.
        Raises ValueError if agent not found (404).
    """
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(f"{api_url}/api/guard/agent/{agent_id}/state")

            if response.status_code == 404:
                raise ValueError(f"Agent {agent_id} not found on server. Agent may not be provisioned.")

            response.raise_for_status()
            return response.json()

    except httpx.ConnectError as e:
        logger.warning(f"Server unreachable: {e}")
        return None
    except httpx.TimeoutException as e:
        logger.warning(f"Server timeout: {e}")
        return None
    except httpx.HTTPStatusError as e:
        if e.response.status_code == 404:
            raise ValueError(f"Agent {agent_id} not found on server.")
        logger.warning(f"Server error: {e}")
        return None


def _get_local_chain_state_sync(agent_id: str) -> Optional[Dict[str, Any]]:
    """Synchronous helper - do not call from async context directly."""
    try:
        from lockstock_guard.client import get_chain_state
        return get_chain_state(agent_id)
    except Exception as e:
        logger.warning(f"Failed to get local chain state: {e}")
        return None


async def get_local_chain_state(agent_id: str) -> Optional[Dict[str, Any]]:
    """
    Get local chain state from Guard daemon (async-safe).

    Uses run_in_threadpool to avoid blocking the event loop,
    since the Guard client does blocking Unix socket I/O.

    Returns:
        Dict with current_hash, sequence, state_matrix, etc.
        Returns None if chain not initialized.
    """
    return await run_in_threadpool(_get_local_chain_state_sync, agent_id)


def _resync_daemon_from_server_sync(agent_id: str) -> bool:
    """Synchronous helper - do not call from async context directly."""
    try:
        from lockstock_guard.client import sync_chain
        result = sync_chain(agent_id)

        if result.get("success"):
            logger.info(
                f"Daemon resync successful: seq={result.get('sequence')} "
                f"hash={result.get('current_hash', '')[:16]}..."
            )
            return True
        else:
            logger.error(f"Daemon resync failed: {result}")
            return False

    except Exception as e:
        logger.error(f"Failed to resync daemon from server: {e}")
        return False


async def resync_daemon_from_server(agent_id: str) -> bool:
    """
    Tell Guard daemon to resync chain state from LockStock server (async-safe).

    Uses run_in_threadpool to avoid blocking the event loop,
    since the Guard client does blocking Unix socket I/O.

    This uses the daemon's sync_chain() which:
    1. Fetches current state from server
    2. Updates daemon's in-memory state
    3. Persists to chain_state.json

    IMPORTANT: Writing chain_state.json directly does NOT work because
    the daemon caches state in memory and only loads from file at startup.

    Returns:
        True if successful, False otherwise.
    """
    return await run_in_threadpool(_resync_daemon_from_server_sync, agent_id)


async def startup_resync(agent_id: str, api_url: str) -> bool:
    """
    Compare local chain state against server authority.

    This is called on adapter startup BEFORE loading middleware.

    Returns:
        True if in sync (or successfully resynced).
        False if unrecoverable (agent not found, irreconcilable state).
    """
    global _sync_status, _last_server_state

    logger.info(f"Starting chain resync for agent {agent_id}...")

    # Fetch server state
    try:
        server_state = await fetch_server_state(agent_id, api_url)
    except ValueError as e:
        # Agent not found on server - FATAL
        logger.critical(f"FATAL: {e}")
        _sync_status = "AGENT_NOT_FOUND"
        return False

    if server_state is None:
        # Server unreachable - proceed optimistically
        logger.warning("Server unreachable during startup resync. Proceeding with local state.")
        logger.warning("The /verify calls will catch any real chain issues.")
        _sync_status = "SERVER_UNREACHABLE"
        return True

    _last_server_state = server_state
    server_seq = server_state.get("last_sequence", 0)
    server_hash = server_state.get("last_hash", "")

    logger.info(f"Server state: seq={server_seq} hash={server_hash[:16] if server_hash else 'none'}...")

    # Get local state
    local_state = await get_local_chain_state(agent_id)

    if local_state is None:
        # No local state - initialize from server
        logger.info("No local chain state found. Initializing from server.")
        if await resync_daemon_from_server(agent_id):
            _sync_status = "INITIALIZED"
            return True
        else:
            _sync_status = "INIT_FAILED"
            return False

    local_seq = local_state.get("sequence", 0)
    local_hash = local_state.get("current_hash", "")

    logger.info(f"Local state: seq={local_seq} hash={local_hash[:16] if local_hash else 'none'}...")

    # Case 1: Perfect sync
    if local_seq == server_seq and local_hash == server_hash:
        logger.info("Chain state IN SYNC with server.")
        _sync_status = "IN_SYNC"
        return True

    # Case 2: Both at genesis (sequence 0)
    if local_seq == 0 and server_seq == 0:
        logger.info("Both local and server at genesis. Fresh agent, proceeding.")
        _sync_status = "FRESH"
        return True

    # Case 3: Local is behind server
    if local_seq < server_seq:
        logger.warning(f"LOCAL IS BEHIND: local_seq={local_seq} < server_seq={server_seq}")
        logger.warning("Chain entries were submitted from elsewhere, or local state was reset.")
        logger.info("Resyncing from server...")

        if await resync_daemon_from_server(agent_id):
            # Verify the fix
            new_local = await get_local_chain_state(agent_id)
            if new_local and new_local.get("sequence") == server_seq:
                logger.info("Resync successful. Local now matches server.")
                _sync_status = "RESYNCED_FROM_BEHIND"
                return True
            else:
                logger.error("Resync verification failed. Daemon may need restart.")
                _sync_status = "RESYNC_VERIFY_FAILED"
                # Still return True - the daemon will reload on next operation
                return True
        else:
            _sync_status = "RESYNC_FAILED"
            return False

    # Case 4: Local is ahead of server
    if local_seq > server_seq:
        logger.warning(f"LOCAL IS AHEAD: local_seq={local_seq} > server_seq={server_seq}")
        logger.warning("Entries were signed locally but never confirmed by server.")
        logger.warning("The pending entries are orphaned. Resyncing from server...")

        if await resync_daemon_from_server(agent_id):
            logger.info("Resync successful. Orphaned entries discarded.")
            _sync_status = "RESYNCED_FROM_AHEAD"
            return True
        else:
            _sync_status = "RESYNC_FAILED"
            return False

    # Case 5: Same sequence but different hash - FORK
    if local_seq == server_seq and local_hash != server_hash:
        logger.critical(f"FORK DETECTED: Same sequence {local_seq} but different hashes!")
        logger.critical(f"  Local hash:  {local_hash}")
        logger.critical(f"  Server hash: {server_hash}")
        logger.critical("This is the worst case. Overwriting local with server state.")

        if await resync_daemon_from_server(agent_id):
            logger.warning("Fork resolved by accepting server state. All pending entries are invalid.")
            _sync_status = "FORK_RESOLVED"
            return True
        else:
            _sync_status = "FORK_UNRESOLVED"
            return False

    # Should not reach here
    logger.error("Unexpected state comparison result")
    _sync_status = "UNKNOWN_STATE"
    return False


# =============================================================================
# PERIODIC HEALTH CHECK (Phase 3c)
# =============================================================================

async def periodic_health_check(agent_id: str, api_url: str):
    """
    Background task that runs every HEALTH_CHECK_INTERVAL seconds.

    Compares local chain state against server and logs warnings on drift.
    Does NOT auto-fix - the per-request /verify will catch and recover.
    """
    global _sync_status, _last_server_state

    while True:
        await asyncio.sleep(HEALTH_CHECK_INTERVAL)

        try:
            server_state = await fetch_server_state(agent_id, api_url)

            if server_state is None:
                logger.warning("Health check: Server unreachable")
                _sync_status = "SERVER_UNREACHABLE"
                continue

            _last_server_state = server_state
            server_seq = server_state.get("last_sequence", 0)
            server_hash = server_state.get("last_hash", "")

            local_state = await get_local_chain_state(agent_id)

            if local_state is None:
                logger.warning("Health check: Local chain state unavailable")
                _sync_status = "LOCAL_UNAVAILABLE"
                continue

            local_seq = local_state.get("sequence", 0)
            local_hash = local_state.get("current_hash", "")

            if local_seq == server_seq and local_hash == server_hash:
                _sync_status = "IN_SYNC"
                logger.debug(f"Health check: IN_SYNC at seq={local_seq}")
            elif local_seq != server_seq:
                _sync_status = "DRIFT"
                logger.warning(
                    f"Health check: Chain drift detected! "
                    f"local_seq={local_seq} server_seq={server_seq}"
                )
            else:
                _sync_status = "FORK"
                logger.critical(
                    f"Health check: FORK detected! "
                    f"Same seq={local_seq} but different hashes"
                )

        except Exception as e:
            logger.warning(f"Health check failed: {e}")
            _sync_status = "CHECK_FAILED"


# =============================================================================
# FASTAPI APPLICATION
# =============================================================================

@asynccontextmanager
async def lifespan(app: FastAPI):
    """
    FastAPI lifespan context manager.

    On startup:
    1. Run startup_resync() to validate/fix chain state
    2. Start periodic health check background task

    On shutdown:
    1. Cancel health check task
    """
    global _startup_time, _health_check_task

    _startup_time = time.time()

    # Phase 3a: Startup resync
    logger.info("="*60)
    logger.info("Trinity Adapter with LockStock Chain Authentication (v2)")
    logger.info("="*60)
    logger.info(f"Agent: {AGENT_ID}")
    logger.info(f"Server: {SERVER_URL}")
    logger.info(f"Socket: {SOCKET_PATH}")
    logger.info(f"Stamped routes: {sum(1 for v in PATH_TASKS.values() if v is not None)}")

    resync_ok = await startup_resync(AGENT_ID, SERVER_URL)

    if not resync_ok:
        logger.critical("Startup resync failed. Check agent provisioning.")
        # Don't exit - let the operator see the status endpoint
        # sys.exit(1)

    # Phase 3c: Start periodic health check
    _health_check_task = asyncio.create_task(
        periodic_health_check(AGENT_ID, SERVER_URL)
    )
    logger.info(f"Started periodic health check (interval={HEALTH_CHECK_INTERVAL}s)")

    logger.info("="*60)

    yield

    # Shutdown
    if _health_check_task:
        _health_check_task.cancel()
        try:
            await _health_check_task
        except asyncio.CancelledError:
            pass
        logger.info("Health check task stopped")


app = FastAPI(
    title="Trinity OpenAI Adapter with LockStock (v2)",
    lifespan=lifespan
)


# --- LockStock Middleware ---
try:
    from lockstock_fastapi import LockStockMiddleware

    if SOCKET_PATH and AGENT_ID:
        app.add_middleware(
            LockStockMiddleware,
            guard_socket=SOCKET_PATH,
            agent_id=AGENT_ID,
            server_url=SERVER_URL,
            block_on_failure=True,  # Reject requests if chain auth fails
            timeout=10.0,
            path_tasks=PATH_TASKS,  # Use path-based task routing
        )
        logger.info("LockStock middleware loaded - chain auth ENABLED")
    else:
        logger.warning("LockStock env vars missing, middleware NOT loaded")
except ImportError as e:
    logger.warning(f"lockstock-fastapi not installed: {e}")
except Exception as e:
    logger.warning(f"LockStock middleware failed to load: {e}")


# =============================================================================
# PYDANTIC MODELS
# =============================================================================

class Message(BaseModel):
    role: str
    content: str


class ChatCompletionRequest(BaseModel):
    model: str
    messages: List[Message]
    max_tokens: Optional[int] = 2048
    temperature: Optional[float] = 0.2


# --- New models for expanded endpoints ---

class CheckpointRequest(BaseModel):
    label: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None


class TeleportRequest(BaseModel):
    destination: str
    payload: Dict[str, Any]


class DeployRequest(BaseModel):
    target: str
    version: str
    config: Optional[Dict[str, Any]] = None


class RestartRequest(BaseModel):
    target: str
    reason: Optional[str] = None


class BackupRequest(BaseModel):
    scope: str
    compress: Optional[bool] = True


class RollbackRequest(BaseModel):
    target: str
    to_version: str
    confirm: Optional[bool] = False


class FileReadRequest(BaseModel):
    path: str
    encoding: Optional[str] = "utf-8"


class FileWriteRequest(BaseModel):
    path: str
    content: str
    mode: Optional[str] = "overwrite"


class ShellRequest(BaseModel):
    command: str
    timeout: Optional[int] = 30


class DatabaseRequest(BaseModel):
    query: str
    params: Optional[Dict[str, Any]] = None


class HandoffRequest(BaseModel):
    to_agent: str
    context: Dict[str, Any]
    priority: Optional[str] = "normal"


class DelegateRequest(BaseModel):
    to_agent: str
    task: str
    params: Dict[str, Any]


class ChatCompletionResponseRequest(BaseModel):
    request_id: str
    response: Dict[str, Any]


# --- Trinity Client ---
async def call_trinity(content: str, model: str, max_tokens: int, temperature: float) -> str:
    """Call Trinity model server."""
    endpoint_map = {
        "trinity-fraud": "/analyze/fraud",
        "trinity-spam": "/analyze/spam",
        "trinity-predict": "/analyze/predict",
        "trinity": "/analyze/predict",
    }
    endpoint = endpoint_map.get(model, "/analyze/predict")
    payload = {
        "content": content,
        "max_tokens": max_tokens,
        "temperature": temperature,
    }
    async with httpx.AsyncClient(timeout=120.0) as client:
        response = await client.post(f"{TRINITY_API}{endpoint}", json=payload)
        response.raise_for_status()
        data = response.json()
        return data.get("analysis", str(data))


# =============================================================================
# ENDPOINTS - INFRASTRUCTURE (UNSTAMPED)
# =============================================================================

@app.get("/health")
async def health():
    """Basic health check - excluded from chain."""
    return {
        "status": "ok",
        "adapter": "trinity-openai-lockstock-v2",
        "agent_id": AGENT_ID,
        "sync_status": _sync_status,
        "stamped_routes": sum(1 for v in PATH_TASKS.values() if v is not None),
    }


@app.get("/lockstock/status")
async def lockstock_status():
    """
    Enhanced status endpoint (Phase 3d).

    Shows both local and server state, sync status, and operational metrics.
    Excluded from chain (no audit entry created).
    """
    global _requests_since_boot, _last_verify_result, _last_server_state, _sync_status

    # Get local state
    local_state = await get_local_chain_state(AGENT_ID)
    local_info = None
    if local_state:
        local_info = {
            "sequence": local_state.get("sequence"),
            "hash": local_state.get("current_hash"),
            "matrix": local_state.get("state_matrix"),
        }

    # Get server state (use cached value, refresh if stale)
    server_info = None
    if _last_server_state:
        server_info = {
            "sequence": _last_server_state.get("last_sequence"),
            "hash": _last_server_state.get("last_hash"),
            "matrix": _last_server_state.get("state_matrix"),
            "last_server_timestamp": _last_server_state.get("last_server_timestamp"),
        }
    else:
        # Try to fetch fresh
        try:
            fresh_state = await fetch_server_state(AGENT_ID, SERVER_URL)
            if fresh_state:
                _last_server_state = fresh_state
                server_info = {
                    "sequence": fresh_state.get("last_sequence"),
                    "hash": fresh_state.get("last_hash"),
                    "matrix": fresh_state.get("state_matrix"),
                    "last_server_timestamp": fresh_state.get("last_server_timestamp"),
                }
        except Exception:
            pass

    # Compute sync status if both available
    if local_info and server_info:
        if local_info["sequence"] == server_info["sequence"] and local_info["hash"] == server_info["hash"]:
            _sync_status = "IN_SYNC"
        elif local_info["sequence"] != server_info["sequence"]:
            _sync_status = "DRIFT"
        else:
            _sync_status = "FORK"
    elif server_info is None:
        _sync_status = "SERVER_UNREACHABLE"
    elif local_info is None:
        _sync_status = "LOCAL_UNAVAILABLE"

    uptime = int(time.time() - _startup_time) if _startup_time else 0

    return {
        "agent_id": AGENT_ID,
        "local": local_info,
        "server": server_info,
        "sync_status": _sync_status,
        "uptime_seconds": uptime,
        "requests_since_boot": _requests_since_boot,
        "last_verify_result": _last_verify_result,
        "health_check_interval": HEALTH_CHECK_INTERVAL,
        "server_url": SERVER_URL,
        "socket_path": SOCKET_PATH,
    }


# =============================================================================
# ENDPOINTS - AI (STAMPED)
# =============================================================================

@app.post("/v1/chat/completions")
async def chat_completions(request: ChatCompletionRequest):
    """
    OpenAI-compatible chat completions endpoint.

    Protected by LockStock middleware - creates chain entry with task CHATCOMPLETION.
    """
    global _requests_since_boot, _last_verify_result

    _requests_since_boot += 1

    user_messages = [m for m in request.messages if m.role == "user"]
    if not user_messages:
        raise HTTPException(status_code=400, detail="No user message")

    try:
        trinity_response = await call_trinity(
            user_messages[-1].content,
            request.model,
            request.max_tokens or 2048,
            request.temperature or 0.2,
        )
        _last_verify_result = "accepted"
    except httpx.ReadTimeout:
        raise HTTPException(status_code=504, detail="Trinity model inference timed out")
    except httpx.ConnectError:
        raise HTTPException(status_code=502, detail="Trinity model server unreachable")
    except Exception as e:
        _last_verify_result = "error"
        raise HTTPException(status_code=500, detail=f"Trinity error: {str(e)}")

    return {
        "id": f"chatcmpl-{uuid.uuid4().hex[:12]}",
        "object": "chat.completion",
        "created": int(time.time()),
        "model": request.model,
        "choices": [
            {
                "index": 0,
                "message": {
                    "role": "assistant",
                    "content": trinity_response,
                },
                "finish_reason": "stop",
            }
        ],
        "usage": {
            "prompt_tokens": sum(len(m.content.split()) for m in request.messages),
            "completion_tokens": len(trinity_response.split()),
            "total_tokens": sum(len(m.content.split()) for m in request.messages) + len(trinity_response.split()),
        },
    }


@app.post("/v1/chat/completions/response")
async def chat_completion_response(request: ChatCompletionResponseRequest):
    """
    Record outbound chat completion response on chain.

    Protected by LockStock middleware - creates chain entry with task CHATCOMPLETIONRESPONSE.
    This stamps the RESPONSE side of a chat, complementing the request stamp.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "response_recorded",
        "request_id": request.request_id,
        "timestamp": int(time.time()),
    }


@app.get("/v1/models")
async def list_models():
    """
    OpenAI-compatible models endpoint.

    Protected by LockStock middleware - creates chain entry with task NETWORK.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "object": "list",
        "data": [
            {"id": "trinity", "object": "model", "owned_by": "local"},
            {"id": "trinity-fraud", "object": "model", "owned_by": "local"},
            {"id": "trinity-spam", "object": "model", "owned_by": "local"},
            {"id": "trinity-predict", "object": "model", "owned_by": "local"},
        ]
    }


# =============================================================================
# ENDPOINTS - LIFECYCLE (STAMPED)
# =============================================================================

@app.get("/v1/heartbeat")
async def heartbeat():
    """
    Periodic health signal - advances chain without action.

    Protected by LockStock middleware - creates chain entry with task HEARTBEAT.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "alive",
        "timestamp": int(time.time()),
        "agent_id": AGENT_ID,
    }


@app.post("/v1/checkpoint")
async def checkpoint(request: CheckpointRequest):
    """
    Record checkpoint audit marker on chain.

    Protected by LockStock middleware - creates chain entry with task CHECKPOINT.
    This is an audit marker - no actual state is persisted by this endpoint.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "checkpoint_recorded",
        "label": request.label,
        "metadata": request.metadata,
        "timestamp": int(time.time()),
    }


@app.post("/v1/teleport")
async def teleport(request: TeleportRequest):
    """
    Queue agent teleport to another location.

    Protected by LockStock middleware - creates chain entry with task TELEPORT.
    Actual transport is not implemented - this is a stub that records intent on chain.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "teleport_queued",
        "destination": request.destination,
        "payload_keys": list(request.payload.keys()),
        "timestamp": int(time.time()),
    }


# =============================================================================
# ENDPOINTS - OPS (STAMPED)
# =============================================================================

@app.post("/v1/ops/deploy")
async def deploy(request: DeployRequest):
    """
    Initiate deployment operation.

    Protected by LockStock middleware - creates chain entry with task DEPLOY.
    Actual deployment is not implemented - this is a stub that records intent on chain.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "deploy_initiated",
        "target": request.target,
        "version": request.version,
        "timestamp": int(time.time()),
    }


@app.post("/v1/ops/restart")
async def restart(request: RestartRequest):
    """
    Initiate restart operation.

    Protected by LockStock middleware - creates chain entry with task RESTART.
    Actual restart is not implemented - this is a stub that records intent on chain.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "restart_initiated",
        "target": request.target,
        "reason": request.reason,
        "timestamp": int(time.time()),
    }


@app.post("/v1/ops/backup")
async def backup(request: BackupRequest):
    """
    Initiate backup operation.

    Protected by LockStock middleware - creates chain entry with task BACKUP.
    Actual backup is not implemented - this is a stub that records intent on chain.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "backup_initiated",
        "scope": request.scope,
        "compress": request.compress,
        "timestamp": int(time.time()),
    }


@app.post("/v1/ops/rollback")
async def rollback(request: RollbackRequest):
    """
    Initiate rollback operation.

    Protected by LockStock middleware - creates chain entry with task ROLLBACK.
    Actual rollback is not implemented - this is a stub that records intent on chain.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "rollback_initiated",
        "target": request.target,
        "to_version": request.to_version,
        "timestamp": int(time.time()),
    }


# =============================================================================
# ENDPOINTS - TOOLS (STAMPED - STUBS ONLY)
# =============================================================================

@app.post("/v1/tools/file/read")
async def file_read(request: FileReadRequest):
    """
    Record file read intent on chain.

    Protected by LockStock middleware - creates chain entry with task FILEREAD.
    SECURITY: This is a STUB. It does NOT actually read files.
    Real implementation requires security review.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "completed",
        "path": request.path,
        "content": "stub - not implemented",
        "note": "File read is stubbed for security. Real implementation pending.",
    }


@app.post("/v1/tools/file/write")
async def file_write(request: FileWriteRequest):
    """
    Record file write intent on chain.

    Protected by LockStock middleware - creates chain entry with task FILEWRITE.
    SECURITY: This is a STUB. It does NOT actually write files.
    Real implementation requires security review.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "completed",
        "path": request.path,
        "bytes_written": 0,
        "note": "File write is stubbed for security. Real implementation pending.",
    }


@app.post("/v1/tools/shell")
async def shell(request: ShellRequest):
    """
    Record shell command intent on chain.

    Protected by LockStock middleware - creates chain entry with task SHELL.
    SECURITY: This is a STUB. It does NOT actually execute commands.
    Real implementation requires security review and command whitelisting.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "completed",
        "command": request.command,
        "output": "stub - not implemented",
        "exit_code": 0,
        "note": "Shell execution is stubbed for security. Real implementation pending.",
    }


@app.post("/v1/tools/database")
async def database(request: DatabaseRequest):
    """
    Record database query intent on chain.

    Protected by LockStock middleware - creates chain entry with task DATABASE.
    SECURITY: This is a STUB. It does NOT actually query databases.
    Real implementation requires security review.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "completed",
        "query": request.query,
        "rows": [],
        "note": "Database query is stubbed for security. Real implementation pending.",
    }


# =============================================================================
# ENDPOINTS - AGENT-TO-AGENT (STAMPED)
# =============================================================================

@app.post("/v1/a2a/handoff")
async def handoff(request: HandoffRequest):
    """
    Initiate handoff to another agent.

    Protected by LockStock middleware - creates chain entry with task HANDOFF.
    Cross-chain reference: This stamps Trinity's chain with intent to hand off.
    The receiving agent would stamp their chain with DELEGATE.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "handoff_initiated",
        "to_agent": request.to_agent,
        "context_keys": list(request.context.keys()),
        "priority": request.priority,
        "timestamp": int(time.time()),
    }


@app.post("/v1/a2a/delegate")
async def delegate(request: DelegateRequest):
    """
    Accept delegated task from another agent.

    Protected by LockStock middleware - creates chain entry with task DELEGATE.
    Cross-chain reference: The source agent would have stamped HANDOFF on their chain.
    """
    global _requests_since_boot
    _requests_since_boot += 1

    return {
        "status": "delegated",
        "to_agent": request.to_agent,
        "task": request.task,
        "params_keys": list(request.params.keys()),
        "timestamp": int(time.time()),
    }


# =============================================================================
# STARTUP
# =============================================================================

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8002)
