import type { McpTool } from "../types.js";
import { existsSync } from "node:fs";
import { join } from "node:path";

export const scaffoldTools: McpTool[] = [
  {
    name: "scaffold_openclaw_sandbox",
    description:
      "Generate Docker/WSL2/Podman setup files for running OpenClaw in an isolated environment. " +
      "Creates Dockerfile (regular user, limited file access, no internet by default, CPU/memory caps), " +
      "docker-compose.yml, and security policy templates. Preview mode by default — see files before writing.",
    inputSchema: {
      type: "object",
      properties: {
        projectPath: {
          type: "string",
          description: "Where to generate sandbox files",
        },
        deployment: {
          type: "string",
          enum: ["docker", "wsl2", "podman"],
          description: "Container runtime (default: docker)",
        },
        allowNetwork: {
          type: "boolean",
          description: "Allow container network access (default: false — most secure)",
        },
        mountPaths: {
          type: "array",
          items: { type: "string" },
          description:
            "Host paths to mount READ-ONLY into the container. " +
            "Never mount home directory or credential stores.",
        },
        memoryLimit: {
          type: "string",
          description: "Container memory limit (default: '512m')",
        },
        cpuLimit: {
          type: "string",
          description: "Container CPU limit (default: '1.0')",
        },
        dryRun: {
          type: "boolean",
          description: "Preview files without writing (default: true)",
        },
      },
      required: ["projectPath"],
    },
    handler: async (args: any) => {
      const projectPath: string = args.projectPath;
      const deployment: string = args.deployment ?? "docker";
      const allowNetwork: boolean = args.allowNetwork ?? false;
      const mountPaths: string[] = args.mountPaths ?? [];
      const memoryLimit: string = args.memoryLimit ?? "512m";
      const cpuLimit: string = args.cpuLimit ?? "1.0";
      const dryRun: boolean = args.dryRun !== false;

      const files: Record<string, string> = {};

      // ── Dockerfile ────────────────────────────────────────────
      files["Dockerfile.openclaw-sandbox"] = `# OpenClaw Sandbox Container
# Generated by openclaw-mcp-nodebench
# Security: non-root user, restricted filesystem, no capabilities

FROM node:22-slim AS base

# Security: create non-root user
RUN groupadd -r openclaw && useradd -r -g openclaw -m -s /bin/bash openclaw

# Install OpenClaw
RUN npm install -g openclaw@latest

# Drop all Linux capabilities
# This prevents privilege escalation even if the process is compromised
USER openclaw
WORKDIR /home/openclaw

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \\
  CMD openclaw --version || exit 1

# Default: run OpenClaw MCP server on stdio
ENTRYPOINT ["openclaw", "serve", "--mcp"]
`;

      // ── docker-compose.yml ────────────────────────────────────
      const volumeMounts = mountPaths
        .map((p) => `      - "${p}:/mnt/${p.split(/[/\\]/).pop()}:ro"`)
        .join("\n");

      files["docker-compose.openclaw-sandbox.yml"] = `# OpenClaw Sandbox — Docker Compose
# Generated by openclaw-mcp-nodebench
# Security: no network${allowNetwork ? " (OVERRIDDEN — network enabled)" : ""}, read-only mounts, resource limits

services:
  openclaw-sandbox:
    build:
      context: .
      dockerfile: Dockerfile.openclaw-sandbox
    container_name: openclaw-sandbox
    restart: "no"

    # Resource limits
    deploy:
      resources:
        limits:
          memory: ${memoryLimit}
          cpus: "${cpuLimit}"

    # Security: read-only root filesystem
    read_only: true
    tmpfs:
      - /tmp:size=100m

    # Security: drop all capabilities, no new privileges
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

    # Network isolation
    ${allowNetwork ? "# Network enabled (user override)" : "network_mode: none"}

    # Volumes (read-only mounts)
    volumes:
${volumeMounts || "      # No host volumes mounted (most secure)"}

    # Environment (no secrets in compose — use .env file)
    env_file:
      - .env.openclaw-sandbox
`;

      // ── .env template ─────────────────────────────────────────
      files[".env.openclaw-sandbox"] = `# OpenClaw Sandbox Environment
# Generated by openclaw-mcp-nodebench
# NEVER commit this file to version control

# OpenClaw configuration
OPENCLAW_SANDBOX=true
OPENCLAW_LOG_LEVEL=info

# API keys (only add what the sandbox needs)
# OPENAI_API_KEY=
# ANTHROPIC_API_KEY=
# GOOGLE_API_KEY=
`;

      // ── Security policy template ──────────────────────────────
      files["sandbox-policy.json"] = JSON.stringify(
        {
          policyName: "default-sandbox",
          allowedTools: [
            "web_search",
            "read_file",
            "summarize",
            "extract_data",
          ],
          blockedTools: [],
          maxCalls: 100,
          maxDurationMin: 30,
          maxConcurrent: 1,
          monitoringLevel: "standard",
          forbiddenPatterns: [],
          requireApproval: ["send_email", "post_to_api"],
          _note:
            "Customize this policy and load it with configure_sandbox_policy.",
        },
        null,
        2
      );

      // ── .gitignore addition ───────────────────────────────────
      files[".gitignore.openclaw"] = `# OpenClaw Sandbox
.env.openclaw-sandbox
*.db
`;

      if (dryRun) {
        return {
          dryRun: true,
          message: "Preview of files to be generated. Re-run with dryRun: false to write.",
          projectPath,
          deployment,
          fileCount: Object.keys(files).length,
          files: Object.entries(files).map(([name, content]) => ({
            name,
            path: join(projectPath, name),
            lines: content.split("\n").length,
            preview: content.slice(0, 200) + (content.length > 200 ? "..." : ""),
          })),
          securityFeatures: [
            "Non-root user (openclaw)",
            "All Linux capabilities dropped",
            "Read-only root filesystem",
            `Network: ${allowNetwork ? "ENABLED (less secure)" : "DISABLED (recommended)"}`,
            `Memory limit: ${memoryLimit}`,
            `CPU limit: ${cpuLimit}`,
            `Host mounts: ${mountPaths.length} (read-only)`,
            "No new privileges flag",
          ],
          quickRef: {
            nextAction: "Review files, then re-run with dryRun: false to write.",
            nextTools: ["scaffold_openclaw_sandbox"],
            methodology: "agent_security",
          },
        };
      }

      // Write files
      const { writeFileSync, mkdirSync } = await import("node:fs");
      mkdirSync(projectPath, { recursive: true });

      const written: string[] = [];
      for (const [name, content] of Object.entries(files)) {
        const filePath = join(projectPath, name);
        if (!existsSync(filePath)) {
          writeFileSync(filePath, content, "utf-8");
          written.push(name);
        }
      }

      return {
        success: true,
        projectPath,
        filesWritten: written,
        filesSkipped: Object.keys(files).length - written.length,
        message: `${written.length} sandbox files created. Build with: docker compose -f docker-compose.openclaw-sandbox.yml build`,
        quickRef: {
          nextAction: "Build the container, then connect with connect_openclaw.",
          nextTools: ["connect_openclaw", "configure_sandbox_policy"],
          methodology: "agent_security",
        },
      };
    },
  },

  {
    name: "scaffold_openclaw_project",
    description:
      "Generate a starter project for OpenClaw + NodeBench integration. " +
      "Creates .mcp.json config, AGENTS.md with OpenClaw safety rules, " +
      "sample workflow definitions, and sandbox policy templates.",
    inputSchema: {
      type: "object",
      properties: {
        projectPath: {
          type: "string",
          description: "Where to generate the project",
        },
        projectName: {
          type: "string",
          description: "Name of the project",
        },
        dryRun: {
          type: "boolean",
          description: "Preview files without writing (default: true)",
        },
      },
      required: ["projectPath", "projectName"],
    },
    handler: async (args: any) => {
      const projectPath: string = args.projectPath;
      const projectName: string = args.projectName;
      const dryRun: boolean = args.dryRun !== false;

      const files: Record<string, string> = {};

      // ── .mcp.json ─────────────────────────────────────────────
      files[".mcp.json"] = JSON.stringify(
        {
          mcpServers: {
            "openclaw-mcp-nodebench": {
              command: "npx",
              args: ["@homenshum/openclaw-mcp-nodebench"],
            },
          },
        },
        null,
        2
      );

      // ── AGENTS.md ─────────────────────────────────────────────
      files["AGENTS.md"] = `# ${projectName} — Agent Instructions

## OpenClaw Safety Protocol

1. **ALWAYS configure a sandbox policy** before connecting to OpenClaw
2. **NEVER use \`allowedTools: ['*']\`** — always explicit allowlists
3. **ALWAYS review the audit trail** after every session
4. **If anomalies detected** — disconnect immediately and investigate
5. Resource budgets are **hard limits** — sessions auto-terminate on exhaustion

## Workflow

1. \`check_openclaw_status\` — verify system readiness
2. \`configure_sandbox_policy\` — define security policy
3. \`connect_openclaw\` — start sandboxed session
4. \`call_openclaw_skill\` — invoke skills safely
5. \`get_openclaw_audit\` — review audit trail
6. \`get_session_compliance\` — check compliance grade
7. \`disconnect_openclaw\` — end session with summary

## Gotcha DB

Use \`search_openclaw_gotchas\` to check for known pitfalls before implementing.
Use \`record_openclaw_gotcha\` to record new discoveries for future sessions.
`;

      // ── Sample workflow ────────────────────────────────────────
      files["workflows/sample-web-research.json"] = JSON.stringify(
        {
          name: "web-research",
          description: "Research a topic using web search and summarize findings",
          steps: [
            { id: "search", type: "navigate", config: { action: "web_search", query: "{{topic}}" } },
            { id: "extract", type: "extract", config: { action: "extract_data", format: "summary" } },
            { id: "save", type: "wait", config: { action: "save_results", timeout: 5000 } },
          ],
          timeoutMs: 60000,
          maxRetries: 2,
        },
        null,
        2
      );

      if (dryRun) {
        return {
          dryRun: true,
          message: "Preview of project files. Re-run with dryRun: false to write.",
          projectPath,
          projectName,
          fileCount: Object.keys(files).length,
          files: Object.entries(files).map(([name]) => ({
            name,
            path: join(projectPath, name),
          })),
          quickRef: {
            nextAction: "Review files, then write with dryRun: false.",
            nextTools: ["scaffold_openclaw_project"],
            methodology: "agent_security",
          },
        };
      }

      const { writeFileSync, mkdirSync } = await import("node:fs");
      mkdirSync(join(projectPath, "workflows"), { recursive: true });

      const written: string[] = [];
      for (const [name, content] of Object.entries(files)) {
        const filePath = join(projectPath, name);
        if (!existsSync(filePath)) {
          writeFileSync(filePath, content, "utf-8");
          written.push(name);
        }
      }

      return {
        success: true,
        projectPath,
        projectName,
        filesWritten: written,
        quickRef: {
          nextAction: "Install dependencies and configure sandbox policy.",
          nextTools: ["configure_sandbox_policy", "check_openclaw_status"],
          methodology: "agent_security",
        },
      };
    },
  },
];
