#!/usr/bin/env node

/**
 * Posimai Lab MCP Server
 * 
 * Provides File System and Gitea integration for the AI Agent.
 * Communication: Stdio (Standard Input/Output)
 * Scope: 
 *   - /app (Server code)
 *   - /data/gitea_files (Gitea Repo Data)
 */

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";

// Define allowed scopes for security
const ALLOWED_Root = "/data/gitea_files";
const APP_Root = "/app";

// Helper to validate paths
function validatePath(requestedPath) {
  const resolved = path.resolve(requestedPath);
  if (resolved.startsWith(ALLOWED_Root) || resolved.startsWith(APP_Root)) {
    return resolved;
  }
  throw new Error(`Access denied: Path ${resolved} is outside allowed scopes.`);
}

// Create Server Instance
const server = new Server(
  {
    name: "posimai-mcp-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

/**
 * Tool Definitions
 */
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "list_files",
        description: "List files in a directory within the allowed scope.",
        inputSchema: zodToJsonSchema(
          z.object({
            path: z.string().describe("Directory path to list (e.g., /data/gitea_files)"),
          })
        ),
      },
      {
        name: "read_file",
        description: "Read the content of a text file.",
        inputSchema: zodToJsonSchema(
          z.object({
            path: z.string().describe("File path to read"),
          })
        ),
      },
      {
        name: "write_file",
        description: "Write content to a file (overwrite).",
        inputSchema: zodToJsonSchema(
          z.object({
            path: z.string().describe("File path to write"),
            content: z.string().describe("Content to write"),
          })
        ),
      },
      {
        name: "hello_world",
        description: "Simple test tool to verify connection.",
        inputSchema: zodToJsonSchema(z.object({})),
      },
    ],
  };
});

/**
 * Tool Execution Handler
 */
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    switch (name) {
      case "hello_world":
        return {
          content: [
            {
              type: "text",
              text: "Hello from Synology! MCP Server is running successfully. 🚀",
            },
          ],
        };

      case "list_files": {
        const dirPath = validatePath(String(args.path));
        const files = await fs.readdir(dirPath, { withFileTypes: true });
        const listing = files.map((f) => 
          `${f.isDirectory() ? "[DIR]" : "[FILE]"} ${f.name}`
        ).join("\n");
        return {
          content: [{ type: "text", text: listing || "(Empty Directory)" }],
        };
      }

      case "read_file": {
        const filePath = validatePath(String(args.path));
        const content = await fs.readFile(filePath, "utf-8");
        return {
          content: [{ type: "text", text: content }],
        };
      }

      case "write_file": {
        const filePath = validatePath(String(args.path));
        await fs.writeFile(filePath, String(args.content), "utf-8");
        return {
          content: [{ type: "text", text: `Successfully wrote to ${filePath}` }],
        };
      }

      default:
        throw new Error(`Unknown tool: ${name}`);
    }
  } catch (error) {
    return {
      content: [
        {
          type: "text",
          text: `Error: ${error instanceof Error ? error.message : String(error)}`,
        },
      ],
      isError: true,
    };
  }
});

// Start Server
async function run() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP Server running on stdio");
}

run().catch((error) => {
  console.error("Fatal error running server:", error);
  process.exit(1);
});


// --- Helper for Zod to JSON Schema (Simplified) ---
function zodToJsonSchema(zodObj) {
    // In a real app we'd use 'zod-to-json-schema' package, 
    // but for simplicity/dependencies we'll do basic mapping here or use the loose definition.
    // Actually, let's keep it simple for now as the strict schema isn't strictly enforced by the starter client yet,
    // but better to specific properties.
    
    // Quick-and-dirty conversion for the simple objects we use above
    if (zodObj._def.typeName === 'ZodObject') {
        const properties = {};
        const required = [];
        for (const [key, schema] of Object.entries(zodObj.shape)) {
            properties[key] = { type: 'string', description: schema.description };
            if (!schema.isOptional()) required.push(key);
        }
        return { type: 'object', properties, required };
    }
    return { type: 'object', properties: {} };
}
