# ============================================================================
# RATIONALBLOKS MCP - Unified Entry Point
# ============================================================================
# Copyright 2026 RationalBloks. All Rights Reserved.
#
# Unified MCP server supporting 3 modes:
#   - backend:  18 API/database tools (projects, schemas, deployments)
#   - frontend: 6 bootstrap tools (thin layer - AI writes the views)
#   - full:     All 24 tools combined (DEFAULT)
#
# FRONTEND PHILOSOPHY (v0.5.0):
# The frontend MCP is a THIN LAYER that provides guardrails, not generation.
# The AI agent writes all views, forms, and custom UI.
# The MCP ensures the AI follows THE ONE WAY architecture.
#
# Usage:
#   export RATIONALBLOKS_API_KEY=rb_sk_your_key_here
#   export RATIONALBLOKS_MODE=full  # or backend, frontend
#   rationalbloks-mcp
#
# Entry Points:
#   rationalbloks-mcp          - Uses RATIONALBLOKS_MODE (default: full)
#   rationalbloks-mcp-backend  - Backend mode only
#   rationalbloks-mcp-frontend - Frontend mode only
#
# Environment Variables:
#   RATIONALBLOKS_API_KEY - Your API key (required for STDIO mode)
#   RATIONALBLOKS_MODE    - Mode: backend, frontend, full (default: full)
#   TRANSPORT             - Transport: stdio (default) or http
# ============================================================================

import os
import sys
from typing import Literal

# Version from package metadata
from importlib.metadata import version as _get_version
__version__ = _get_version("rationalbloks-mcp")

# Mode type
Mode = Literal["backend", "frontend", "full"]

# Public API
__all__ = [
    "__version__",
    "main",
    "main_backend",
    "main_frontend",
    "Mode",
]


def _get_mode() -> Mode:
    # Get mode from environment variable
    # Returns: Mode string: "backend", "frontend", or "full"
    mode = os.environ.get("RATIONALBLOKS_MODE", "full").lower()
    if mode not in ("backend", "frontend", "full"):
        print(f"WARNING: Invalid RATIONALBLOKS_MODE '{mode}', using 'full'", file=sys.stderr)
        mode = "full"
    return mode  # type: ignore


def _validate_api_key(api_key: str | None, transport: str) -> str | None:
    # Validate API key for the given transport
    # HTTP mode: API key provided per-request (returns None)
    # STDIO mode: API key required at startup (returns validated key)
    # Raises SystemExit if API key invalid for STDIO mode
    # HTTP mode: API key provided per-request
    if transport == "http":
        return None
    
    # STDIO mode: API key required at startup
    if not api_key:
        print("ERROR: RATIONALBLOKS_API_KEY environment variable not set", file=sys.stderr)
        print("", file=sys.stderr)
        print("Get your API key from: https://rationalbloks.com/settings", file=sys.stderr)
        print("", file=sys.stderr)
        print("Then set it:", file=sys.stderr)
        print("  export RATIONALBLOKS_API_KEY=rb_sk_your_key_here", file=sys.stderr)
        sys.exit(1)
    
    if not api_key.startswith("rb_sk_"):
        print("ERROR: Invalid API key format. Must start with 'rb_sk_'", file=sys.stderr)
        sys.exit(1)
    
    return api_key


def _run_server(mode: Mode) -> None:
    # Run MCP server in the specified mode
    # mode: "backend", "frontend", or "full"
    api_key = os.environ.get("RATIONALBLOKS_API_KEY")
    transport = os.environ.get("TRANSPORT", "stdio").lower()
    http_mode = transport == "http"
    
    # Validate API key
    validated_key = _validate_api_key(api_key, transport)
    
    try:
        if mode == "backend":
            from .backend import create_backend_server
            server = create_backend_server(api_key=validated_key, http_mode=http_mode)
        elif mode == "frontend":
            from .frontend import create_frontend_server
            server = create_frontend_server(api_key=validated_key, http_mode=http_mode)
        else:  # full mode
            server = _create_full_server(api_key=validated_key, http_mode=http_mode)
        
        server.run(transport=transport)
        
    except KeyboardInterrupt:
        sys.exit(0)
    except Exception as e:
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)


def _create_full_server(api_key: str | None, http_mode: bool):
    # Create a full server with all 24 tools
    # Combines backend (18 tools) + frontend (6 tools)
    from .core import BaseMCPServer
    from .backend.tools import BACKEND_TOOLS, BACKEND_PROMPTS
    from .backend.client import LogicBlokClient
    from .frontend.tools import FRONTEND_TOOLS, FRONTEND_PROMPTS
    from .frontend.client import FrontendClient
    
    FULL_INSTRUCTIONS = """RationalBloks MCP Server - Full Mode

🚀 BUILD COMPLETE FULLSTACK APPS WITH AI CREATIVITY

THE PHILOSOPHY:
- Backend MCP: 18 deterministic tools for API/database/deployment
- Frontend MCP: 6 bootstrap tools (THIN LAYER)
- YOU (the AI): Write all views, forms, and custom UI!

WORKFLOW:
1. Use backend MCP to create_project (API deployed in minutes)
2. Use frontend MCP to bootstrap:
   - clone_template: Fresh React project
   - generate_types: TypeScript from schema
   - generate_api_service: datablokApi.ts (THE ONE WAY glue)
   - configure_api_url: Set backend URL
3. Read get_frontend_guidelines to understand the patterns
4. YOU WRITE the custom views (kanban, calendar, cards - not boring tables!)

FRONTEND TOOLS (6 tools - THIN LAYER):
📖 TEACH:
- get_frontend_guidelines: THE ONE WAY architecture docs
- get_template_structure: Explore template files

🔧 BOOTSTRAP:
- clone_template: Fresh project from GitHub
- generate_types: TypeScript interfaces from schema
- generate_api_service: datablokApi.ts (THE ONE WAY glue)
- configure_api_url: Set .env API URL

BACKEND TOOLS (18 tools):
- Project management: list_projects, get_project, create_project, etc.
- Schema operations: get_schema, update_schema
- Deployment: deploy_staging, deploy_production, rollback_project

SCHEMA FORMAT (FLAT - CRITICAL):
✅ {tasks: {title: {type: "string", max_length: 200}}} 
❌ {tasks: {fields: {title: {type: "string"}}}}

AVAILABLE: 24 tools (18 backend + 6 frontend)"""
    
    class FullMCPServer(BaseMCPServer):
        # Full MCP server with all 24 tools
        
        def __init__(self, api_key: str | None, http_mode: bool):
            super().__init__(
                name="rationalbloks",
                version=__version__,
                instructions=FULL_INSTRUCTIONS,
                api_key=api_key,
                http_mode=http_mode,
            )
            
            # Register all tools
            self.register_tools(BACKEND_TOOLS)
            self.register_tools(FRONTEND_TOOLS)
            
            # Register all prompts
            self.register_prompts(BACKEND_PROMPTS)
            self.register_prompts(FRONTEND_PROMPTS)
            
            # Register handlers
            self.register_tool_handler("*", self._handle_tool)
            
            # Set up MCP handlers
            self.setup_handlers()
        
        def _get_backend_client(self) -> LogicBlokClient:
            api_key = self.get_api_key_for_request()
            if not api_key:
                raise ValueError("No API key available")
            return LogicBlokClient(api_key)
        
        def _get_frontend_client(self) -> FrontendClient:
            api_key = self.get_api_key_for_request()
            return FrontendClient(api_key)
        
        async def _handle_tool(self, name: str, arguments: dict):
            # Route tool calls to appropriate client
            # Backend tools
            backend_tool_names = [t["name"] for t in BACKEND_TOOLS]
            if name in backend_tool_names:
                async with self._get_backend_client() as client:
                    return await self._call_backend_tool(client, name, arguments)
            
            # Frontend tools
            frontend_tool_names = [t["name"] for t in FRONTEND_TOOLS]
            if name in frontend_tool_names:
                # get_frontend_guidelines is handled inline (returns docs)
                if name == "get_frontend_guidelines":
                    from .frontend.tools import THE_ONE_WAY_ARCHITECTURE, COMPONENT_GUIDELINES
                    section = arguments.get("section", "all")
                    if section == "architecture":
                        return {"guidelines": THE_ONE_WAY_ARCHITECTURE}
                    elif section == "components":
                        return {"guidelines": COMPONENT_GUIDELINES}
                    else:
                        return {"guidelines": THE_ONE_WAY_ARCHITECTURE + "\n\n---\n\n" + COMPONENT_GUIDELINES}
                async with self._get_frontend_client() as client:
                    return await self._call_frontend_tool(client, name, arguments)
            
            raise ValueError(f"Unknown tool: {name}")
        
        async def _call_backend_tool(self, client: LogicBlokClient, name: str, args: dict):
            # Call a backend tool
            if name == "list_projects":
                return await client.list_projects()
            elif name == "get_project":
                return await client.get_project(args["project_id"])
            elif name == "get_schema":
                return await client.get_schema(args["project_id"])
            elif name == "get_user_info":
                return await client.get_user_info()
            elif name == "get_job_status":
                return await client.get_job_status(args["job_id"])
            elif name == "get_project_info":
                return await client.get_project_info(args["project_id"])
            elif name == "get_version_history":
                return await client.get_version_history(args["project_id"])
            elif name == "get_template_schemas":
                return await client.get_template_schemas()
            elif name == "get_subscription_status":
                return await client.get_subscription_status()
            elif name == "get_project_usage":
                return await client.get_project_usage(args["project_id"])
            elif name == "get_schema_at_version":
                return await client.get_schema_at_version(args["project_id"], args["version"])
            elif name == "create_project":
                return await client.create_project(
                    name=args["name"],
                    schema=args["schema"],
                    description=args.get("description"),
                )
            elif name == "update_schema":
                return await client.update_schema(args["project_id"], args["schema"])
            elif name == "deploy_staging":
                return await client.deploy_staging(args["project_id"])
            elif name == "deploy_production":
                return await client.deploy_production(args["project_id"])
            elif name == "delete_project":
                return await client.delete_project(args["project_id"])
            elif name == "rollback_project":
                return await client.rollback_project(
                    project_id=args["project_id"],
                    version=args["version"],
                    environment=args.get("environment", "staging"),
                )
            elif name == "rename_project":
                return await client.rename_project(args["project_id"], args["name"])
            else:
                raise ValueError(f"Unknown backend tool: {name}")
        
        async def _call_frontend_tool(self, client: FrontendClient, name: str, args: dict):
            # Call a frontend tool (thin layer - 5 tools only)
            if name == "generate_types":
                return await client.generate_types(
                    project_path=args["project_path"],
                    schema=args["schema"],
                )
            elif name == "generate_api_service":
                return await client.generate_api_service(
                    project_path=args["project_path"],
                    schema=args["schema"],
                    api_url=args.get("api_url"),
                )
            elif name == "clone_template":
                return await client.clone_template(
                    destination=args["destination"],
                    project_name=args["project_name"],
                )
            elif name == "get_template_structure":
                return await client.get_template_structure(
                    path=args.get("path", ""),
                    max_depth=args.get("max_depth", 3),
                )
            elif name == "configure_api_url":
                return await client.configure_api_url(
                    project_path=args["project_path"],
                    api_url=args["api_url"],
                )
            else:
                raise ValueError(f"Unknown frontend tool: {name}")
    
    return FullMCPServer(api_key=api_key, http_mode=http_mode)


# ============================================================================
# ENTRY POINTS
# ============================================================================

def main() -> None:
    # Main entry point - uses RATIONALBLOKS_MODE environment variable
    # Default mode is 'full' (all 24 tools: 18 backend + 6 frontend)
    mode = _get_mode()
    print(f"[rationalbloks-mcp] Starting in {mode} mode...", file=sys.stderr)
    _run_server(mode)


def main_backend() -> None:
    # Backend-only entry point (18 tools)
    print("[rationalbloks-mcp] Starting in backend mode...", file=sys.stderr)
    _run_server("backend")


def main_frontend() -> None:
    # Frontend-only entry point (6 tools - thin layer)
    print("[rationalbloks-mcp] Starting in frontend mode...", file=sys.stderr)
    _run_server("frontend")


if __name__ == "__main__":
    main()
