"""外部 MCP stack 部署前檢查 preview。

本模組只讀本機 compose 設計與環境變數狀態；不執行 docker、SSH、DB migration、
RBAC 建立或任何 MCP server health call。
"""

import os
from pathlib import Path


MCP_COMPOSE_PATH = "docker-compose.mcp.yml"
EXPECTED_MCP_SERVICES = (
    "postgres-mcp",
    "mcp-omnisearch",
    "firecrawl-self",
    "firecrawl-redis",
    "firecrawl-playwright",
    "chrome-reaper",
    "filesystem-mcp",
)
EXPECTED_MCP_CONTAINERS = (
    "momo-mcp-postgres",
    "momo-mcp-omnisearch",
    "momo-mcp-firecrawl",
    "momo-mcp-firecrawl-redis",
    "momo-mcp-firecrawl-playwright",
    "momo-mcp-chrome-reaper",
    "momo-mcp-filesystem",
)
EXPECTED_LOCALHOST_PORTS = ("3001", "3002", "3003", "3004")
REQUIRED_ENV_VARS = ("MCP_POSTGRES_PASSWORD", "TAVILY_API_KEY", "EXA_API_KEY")
OPTIONAL_ENV_VARS = ("FIRECRAWL_AUTH_KEY",)


def _read_compose_file(path):
    compose_path = Path(path)
    if not compose_path.exists():
        return compose_path, ""
    return compose_path, compose_path.read_text(encoding="utf-8")


def _build_env_status(env):
    statuses = []
    for name in REQUIRED_ENV_VARS:
        statuses.append(
            {
                "name": name,
                "required": True,
                "present": bool(env.get(name)),
                "value_redacted": bool(env.get(name)),
            }
        )
    for name in OPTIONAL_ENV_VARS:
        statuses.append(
            {
                "name": name,
                "required": False,
                "present": bool(env.get(name)),
                "value_redacted": bool(env.get(name)),
            }
        )
    return statuses


def build_mcp_deploy_preflight_plan(*, compose_path=MCP_COMPOSE_PATH, env=None):
    """建立外部 MCP 部署 preflight；不執行任何部署動作。"""
    env = env or os.environ
    compose_file, compose_text = _read_compose_file(compose_path)
    service_statuses = [
        {
            "service": service,
            "declared": f"  {service}:" in compose_text,
        }
        for service in EXPECTED_MCP_SERVICES
    ]
    container_statuses = [
        {
            "container": container,
            "declared": f"container_name: {container}" in compose_text,
        }
        for container in EXPECTED_MCP_CONTAINERS
    ]
    port_statuses = [
        {
            "port": port,
            "localhost_only": f"127.0.0.1:{port}:" in compose_text,
            "declared": f":{port}" in compose_text,
        }
        for port in EXPECTED_LOCALHOST_PORTS
    ]
    env_statuses = _build_env_status(env)
    env_required_present = all(
        item["present"]
        for item in env_statuses
        if item["required"]
    )
    router_enabled = env.get("MCP_ROUTER_ENABLED", "false").strip().lower() in (
        "true",
        "1",
        "yes",
        "on",
    )
    checks = {
        "compose_file_present": compose_file.exists(),
        "all_expected_services_declared": all(item["declared"] for item in service_statuses),
        "all_expected_containers_declared": all(item["declared"] for item in container_statuses),
        "all_public_mcp_ports_localhost_only": all(
            item["localhost_only"] for item in port_statuses
        ),
        "required_env_vars_present": env_required_present,
        "postgres_readonly_role_required": "POSTGRES_USER=mcp_readonly" in compose_text,
        "postgres_allowed_tables_limited": "ALLOWED_TABLES=" in compose_text,
        "filesystem_mounts_read_only": "./data:/data:ro" in compose_text and "./logs:/logs:ro" in compose_text,
        "firecrawl_resource_guard_present": "memory: 2g" in compose_text and "PLAYWRIGHT_BROWSER_POOL_MAX=3" in compose_text,
        "chrome_reaper_declared": "chrome-reaper:" in compose_text,
        "router_flag_still_off_before_health": not router_enabled,
        "no_deployment_action_executed": True,
        "no_database_lifecycle_action_executed": True,
    }
    blocked_reasons = [
        key for key, passed in checks.items()
        if not passed
    ]

    return {
        "mode": "mcp_external_deploy_preflight_preview",
        "compose_path": str(compose_file),
        "compose_file_present": compose_file.exists(),
        "expected_services": list(EXPECTED_MCP_SERVICES),
        "service_statuses": service_statuses,
        "container_statuses": container_statuses,
        "port_statuses": port_statuses,
        "env_statuses": env_statuses,
        "checks": checks,
        "ready_to_start_stack": not blocked_reasons,
        "blocked_reasons": blocked_reasons,
        "operator_command_preview": (
            "docker compose -f docker-compose.mcp.yml up -d"
            if compose_file.exists()
            else None
        ),
        "post_start_health_targets": [
            "http://localhost:3001/health",
            "http://localhost:3002/health",
            "http://localhost:3003/health",
            "http://localhost:3004/health",
        ],
        "fallback_plan": [
            {
                "key": "keep_router_disabled",
                "label": "MCP_ROUTER_ENABLED 維持 false，直到四個 health endpoint 全部 200",
            },
            {
                "key": "stop_mcp_stack_only",
                "label": "若 MCP stack 異常，只停止 docker-compose.mcp.yml 管理的 momo-mcp-* 容器",
            },
            {
                "key": "no_momo_db_lifecycle_change",
                "label": "不得 stop/rm/recreate momo-db；postgres-mcp 只能使用 mcp_readonly",
            },
        ],
        "safe_deploy_boundaries": [
            "do_not_use_remove_orphans",
            "do_not_touch_momo_db_container",
            "do_not_enable_mcp_router_before_health_passes",
            "do_not_run_external_crawlers_without_operator_approval",
        ],
        "deployment_actions_executed": False,
        "docker_command_executed": False,
        "ssh_command_executed": False,
        "database_session_created": False,
        "database_write_executed": False,
        "database_commit_executed": False,
        "external_network_executed": False,
        "scheduler_attached": False,
        "writes_executed": False,
        "would_write_database": False,
    }
