"""BundleCompiler — Generate ROSClaw-native Asset Bundles.

Takes an SDK description and emits:
- MCP Server stub
- Skill Manifest (JSON)
- Provider Manifest (JSON)
- Unit tests
- README

All output is validated through a Critic checklist before being marked ready.
"""

from __future__ import annotations

import json
import textwrap
from dataclasses import dataclass, field


@dataclass
class BundleOutput:
    """Result of compiling an asset bundle."""

    bundle_name: str
    files: dict[str, str] = field(default_factory=dict)
    validation: dict[str, bool] = field(default_factory=dict)
    staging_ready: bool = False
    production_ready: bool = False


class BundleCompiler:
    """Compile an Asset Bundle from an SDK description."""

    def compile(self, sdk_doc: str, bundle_name: str) -> BundleOutput:
        """Generate a complete bundle."""
        files: dict[str, str] = {}

        # Derive capability name from bundle_name
        cap_name = bundle_name.replace("-", "_").replace(" ", "_").lower()

        files[f"{bundle_name}/mcp_server.py"] = self._generate_mcp_server(cap_name, sdk_doc)
        files[f"{bundle_name}/skill_manifest.json"] = self._generate_skill_manifest(cap_name, sdk_doc)
        files[f"{bundle_name}/provider_manifest.json"] = self._generate_provider_manifest(cap_name, sdk_doc)
        files[f"{bundle_name}/tests/test_{cap_name}.py"] = self._generate_tests(cap_name)
        files[f"{bundle_name}/README.md"] = self._generate_readme(bundle_name, sdk_doc, cap_name)

        # Critic validation
        validation = self._critic_validate(files)
        # CRITICAL FIX: staging only requires technical checks;
        # safety_approved gates production, not staging.
        staging_checks = {k: v for k, v in validation.items() if k != "safety_approved"}
        staging_ready = all(staging_checks.values())
        # production_ready requires explicit safety approval + human review
        production_ready = False

        return BundleOutput(
            bundle_name=bundle_name,
            files=files,
            validation=validation,
            staging_ready=staging_ready,
            production_ready=production_ready,
        )

    def _generate_mcp_server(self, cap_name: str, sdk_doc: str) -> str:
        return textwrap.dedent(f'''\
            #!/usr/bin/env python3
            """MCP Server for {cap_name} capability.

            Auto-generated by ROSClaw Forge from SDK doc:
            {sdk_doc[:80]}...
            """
            from mcp.server import Server
            from mcp.types import TextContent, Tool

            # CRITICAL FIX: module-level server and tool registration for structural stability
            _server = Server("{cap_name}")

            @_server.list_tools()
            async def _list_tools() -> list[Tool]:
                return [
                    Tool(
                        name="{cap_name}.invoke",
                        description="Invoke the {cap_name} capability",
                        inputSchema={{
                            "type": "object",
                            "properties": {{
                                "param": {{"type": "string", "description": "Input parameter"}}
                            }},
                            "required": ["param"],
                        }},
                    ),
                ]

            class {cap_name.title()}MCPServer:
                def __init__(self):
                    self.server = _server

                async def run(self):
                    from mcp.server.stdio import stdio_server
                    from mcp.server.models import InitializationOptions
                    async with stdio_server() as (read, write):
                        await self.server.run(read, write, InitializationOptions(
                            server_name="{cap_name}", server_version="1.0.0",
                            capabilities=self.server.get_capabilities(),
                        ))
            ''')

    def _generate_skill_manifest(self, cap_name: str, sdk_doc: str) -> str:
        manifest = {
            "skill_id": cap_name,
            "version": "1.0.0",
            "name": cap_name.replace("_", " ").title(),
            "description": sdk_doc[:200],
            "capabilities": [f"skill.{cap_name}"],
            "entry_point": f"{cap_name}.mcp_server:{cap_name.title()}MCPServer",
            "sandbox_safe": True,
            "firewall_hooks": ["pre_check", "post_check"],
        }
        return json.dumps(manifest, indent=2)

    def _generate_provider_manifest(self, cap_name: str, sdk_doc: str) -> str:
        manifest = {
            "name": cap_name,
            "version": "1.0.0",
            "type": "skill",
            "capabilities": [f"skill.{cap_name}"],
            "inputs": {
                "param": {"type": "string", "required": True},
            },
            "outputs": {
                "result": {"type": "object"},
            },
            "safety_level": "MODERATE",
            "async_safe": True,
            "preemptible": True,
        }
        return json.dumps(manifest, indent=2)

    def _generate_tests(self, cap_name: str) -> str:
        return textwrap.dedent(f'''\
            import pytest
            from {cap_name}.mcp_server import {cap_name.title()}MCPServer

            def test_server_init():
                srv = {cap_name.title()}MCPServer()
                assert srv.server is not None

            def test_tool_schema():
                srv = {cap_name.title()}MCPServer()
                import asyncio
                tools = asyncio.run(srv.server.list_tools())
                assert len(tools) >= 1
            ''')

    def _generate_readme(self, bundle_name: str, sdk_doc: str, cap_name: str) -> str:
        return textwrap.dedent(f'''\
            # {bundle_name}

            Auto-generated ROSClaw Asset Bundle.

            ## SDK Source
            {sdk_doc[:200]}...

            ## Files
            - `mcp_server.py` — MCP Server stub
            - `skill_manifest.json` — Skill registration manifest
            - `provider_manifest.json` — Provider routing manifest
            - `tests/test_{cap_name}.py` — Unit tests
            - `README.md` — This file

            ## Installation
            ```bash
            rosclaw forge install {bundle_name} --staging
            ```

            ## Validation
            Run critic before production:
            ```bash
            rosclaw forge validate {bundle_name}
            ```
            ''')

    def _critic_validate(self, files: dict[str, str]) -> dict[str, bool]:
        """Run automated critic checks on generated files."""
        # CRITICAL FIX: detect explicitly unsafe SDK docs and block production
        sdk_doc_lower = ""
        for content in files.values():
            if "SDK:" in content or "sdk" in content.lower():
                sdk_doc_lower = content.lower()
                break
        # Only block explicitly malicious markers; "no safety info" is fine (auto-injected)
        explicitly_unsafe = any(marker in sdk_doc_lower for marker in [
            "unsafe", "bypass firewall", "disable check", "ignore limit",
            "unrestricted", "malicious", "harmful"
        ])

        checks = {
            "async_safe": any("async def" in content for content in files.values()),
            "schema_complete": any("inputSchema" in content or "inputs" in content for content in files.values()),
            "safety_hooks": any("firewall_hooks" in content for content in files.values()),
            "preemption_ready": any("preemptible" in content for content in files.values()),
            "tests_present": any("test_" in fname for fname in files),
            "readme_present": any("README" in fname for fname in files),
            "safety_approved": not explicitly_unsafe,
        }
        return checks
