/**
 * DashCaddy MCP (Model Context Protocol) Server
 *
 * Makes DashCaddy controllable by ANY AI agent — Hermes, Claude, GPT, etc.
 * The AI agent connects to this server and can:
 *   - List and manage services/containers
 *   - Deploy apps from the catalog
 *   - Manage DNS records and Caddyfile routes
 *   - Run diagnostics
 *   - Create backups and restore
 *   - Check system health
 *
 * Protocol: JSON-RPC 2.0 over stdio
 * Spec: https://modelcontextprotocol.io
 *
 * Usage:
 *   node mcp-server.js
 *
 * In an AI agent config (e.g. Claude Desktop):
 *   {
 *     "mcpServers": {
 *       "dashcaddy": {
 *         "command": "node",
 *         "args": ["/path/to/mcp-server.js"],
 *         "env": {
 *           "DASHCADDY_URL": "http://localhost:3001",
 *           "DASHCADDY_API_KEY": "dk_..."
 *         }
 *       }
 *     }
 *   }
 */

const readline = require('readline');

// ─── Configuration ──────────────────────────────────────────────────────────

const BASE_URL = process.env.DASHCADDY_URL || 'http://localhost:3001';
const API_KEY = process.env.DASHCADDY_API_KEY || '';
const MCP_VERSION = '2024-11-05';

// ─── Tool Definitions ───────────────────────────────────────────────────────

const TOOLS = [
  // ── Services ──
  {
    name: 'dashcaddy_list_services',
    description: 'List all services on the DashCaddy dashboard. Returns service ID, name, status (up/down), URL, and health.',
    inputSchema: { type: 'object', properties: {} },
  },
  {
    name: 'dashcaddy_get_service',
    description: 'Get details for a specific service by ID. Includes health history, credentials, and configuration.',
    inputSchema: {
      type: 'object',
      properties: {
        serviceId: { type: 'string', description: 'The service ID (e.g. "plex")' },
      },
      required: ['serviceId'],
    },
  },
  {
    name: 'dashcaddy_check_health',
    description: 'Check the health of all services or a specific service. Returns up/down status, response time, and HTTP status code.',
    inputSchema: {
      type: 'object',
      properties: {
        serviceId: { type: 'string', description: 'Optional: check only this service. Omit for all services.' },
      },
    },
  },

  // ── System ──
  {
    name: 'dashcaddy_system_health',
    description: 'Get overall system health summary. Returns status (healthy/degraded/unhealthy), service counts, memory, disk, and uptime. Great for "is everything OK?" queries.',
    inputSchema: { type: 'object', properties: {} },
  },
  {
    name: 'dashcaddy_system_metrics',
    description: 'Get Prometheus-format metrics for system monitoring. Includes request counts, error rates, memory gauges.',
    inputSchema: { type: 'object', properties: {} },
  },

  // ── Containers ──
  {
    name: 'dashcaddy_list_containers',
    description: 'List all Docker containers (running and stopped). Returns container ID, name, image, status, and ports.',
    inputSchema: {
      type: 'object',
      properties: {
        all: { type: 'boolean', description: 'Include stopped containers (default: true)' },
      },
    },
  },
  {
    name: 'dashcaddy_container_action',
    description: 'Start, stop, restart, or remove a Docker container.',
    inputSchema: {
      type: 'object',
      properties: {
        containerId: { type: 'string', description: 'Container ID or name' },
        action: { type: 'string', enum: ['start', 'stop', 'restart', 'remove'], description: 'Action to perform' },
      },
      required: ['containerId', 'action'],
    },
  },

  // ── Catalog & Discovery ──
  {
    name: 'dashcaddy_search_catalog',
    description: 'Search the app catalog for self-hostable applications. Use this when a user asks "can DashCaddy host X?" or "I want to self-host Y".',
    inputSchema: {
      type: 'object',
      properties: {
        query: { type: 'string', description: 'Search query (e.g. "media streaming", "password manager", "ad blocker")' },
        category: { type: 'string', description: 'Filter by category (media, development, network, database, etc.)' },
      },
    },
  },
  {
    name: 'dashcaddy_discover_services',
    description: 'Auto-detect running Docker containers and suggest adding them to the dashboard. Returns discovered services with suggested configs.',
    inputSchema: { type: 'object', properties: {} },
  },

  // ── Deployment ──
  {
    name: 'dashcaddy_deploy_app',
    description: 'Deploy a self-hosted application from the catalog. This is the main "self-host X" action. Pulls the Docker image, creates the container, generates a Caddyfile reverse proxy route, and adds the service to the dashboard. Returns the URL the user can access.',
    inputSchema: {
      type: 'object',
      properties: {
        templateId: { type: 'string', description: 'App template ID from the catalog (e.g. "plex", "gitea", "nextcloud")' },
        subdomain: { type: 'string', description: 'Subdomain for the service (e.g. "plex" → plex.example.com)' },
        port: { type: 'number', description: 'Override the default port' },
      },
      required: ['templateId'],
    },
  },
  {
    name: 'dashcaddy_wizard_recommend',
    description: 'Get service recommendations based on what the user wants to self-host. Use this when a user describes a goal (e.g. "I want to stream movies" → recommends Plex, Sonarr, Radarr).',
    inputSchema: {
      type: 'object',
      properties: {
        categories: {
          type: 'array',
          items: { type: 'string' },
          description: 'Categories: media-streaming, file-sync, home-network, smart-home, development, monitoring',
        },
        hardwareProfile: { type: 'string', enum: ['minimal', 'medium', 'powerful'], description: 'Hardware capability (default: medium)' },
      },
      required: ['categories'],
    },
  },

  // ── DNS & Proxy ──
  {
    name: 'dashcaddy_list_dns',
    description: 'List DNS records. Useful for "what domains point to this server?"',
    inputSchema: {
      type: 'object',
      properties: {
        zone: { type: 'string', description: 'DNS zone to query (optional)' },
      },
    },
  },
  {
    name: 'dashcaddy_generate_caddyfile',
    description: 'Generate a Caddyfile reverse proxy block from structured config. Useful for setting up custom reverse proxy rules.',
    inputSchema: {
      type: 'object',
      properties: {
        domain: { type: 'string', description: 'Domain name (e.g. "app.example.com")' },
        upstream: { type: 'string', description: 'Upstream address (e.g. "localhost:8080")' },
        websocket: { type: 'boolean', description: 'Enable WebSocket support' },
        cors: { type: 'boolean', description: 'Enable CORS headers' },
        auth: { type: 'boolean', description: 'Enable DashCaddy SSO auth gate' },
      },
      required: ['domain', 'upstream'],
    },
  },

  // ── Diagnostics ──
  {
    name: 'dashcaddy_diagnose',
    description: 'Run diagnostics on a service or the entire system. Checks container logs, resource usage, network connectivity, and health endpoints. Returns structured findings with severity levels.',
    inputSchema: {
      type: 'object',
      properties: {
        serviceId: { type: 'string', description: 'Service to diagnose (omit for system-wide)' },
        depth: { type: 'string', enum: ['quick', 'standard', 'deep'], description: 'Diagnostic depth (default: standard)' },
      },
    },
  },

  // ── Backup & Recovery ──
  {
    name: 'dashcaddy_create_backup',
    description: 'Create a full system backup (services, config, credentials, Caddyfile, themes). Returns the backup data.',
    inputSchema: { type: 'object', properties: {} },
  },
  {
    name: 'dashcaddy_get_backup_status',
    description: 'Check the status of the last backup and restore operations.',
    inputSchema: { type: 'object', properties: {} },
  },

  // ── Fleet ──
  {
    name: 'dashcaddy_list_fleet',
    description: 'List all hosts in the DashCaddy fleet (for multi-server management).',
    inputSchema: { type: 'object', properties: {} },
  },
];

// ─── API Client ─────────────────────────────────────────────────────────────

async function apiCall(method, path, body) {
  const url = `${BASE_URL}/api/v1${path}`;
  const headers = { 'Content-Type': 'application/json' };
  if (API_KEY) headers['x-api-key'] = API_KEY;

  try {
    const response = await fetch(url, {
      method,
      headers,
      body: body ? JSON.stringify(body) : undefined,
    });

    const text = await response.text();
    let data;
    try { data = JSON.parse(text); } catch { data = { raw: text }; }

    if (!response.ok) {
      return {
        error: true,
        status: response.status,
        message: data.error || data.message || `HTTP ${response.status}`,
        code: data.code,
      };
    }

    return data;
  } catch (err) {
    return { error: true, message: err.message, code: 'NETWORK_ERROR' };
  }
}

// ─── Tool Handlers ──────────────────────────────────────────────────────────

async function handleTool(name, args) {
  switch (name) {
    // ── Services ──
    case 'dashcaddy_list_services': {
      const data = await apiCall('GET', '/services');
      if (data.error) return data;
      const services = data.services || data.data || [];
      return {
        count: services.length,
        services: services.map(s => ({
          id: s.id, name: s.name, status: s.status || 'unknown',
          url: s.url, subdomain: s.subdomain, type: s.type,
        })),
      };
    }

    case 'dashcaddy_get_service': {
      return apiCall('GET', `/services/${args.serviceId}`);
    }

    case 'dashcaddy_check_health': {
      if (args.serviceId) {
        return apiCall('GET', `/services/${args.serviceId}/health`);
      }
      return apiCall('GET', '/health/all');
    }

    // ── System ──
    case 'dashcaddy_system_health': {
      // Public endpoint — no auth needed
      const response = await fetch(`${BASE_URL}/api/v1/system/health`);
      return response.json();
    }

    case 'dashcaddy_system_metrics': {
      const response = await fetch(`${BASE_URL}/api/v1/metrics/prometheus`);
      return { metrics: await response.text() };
    }

    // ── Containers ──
    case 'dashcaddy_list_containers': {
      const all = args.all !== false;
      return apiCall('GET', `/containers?all=${all}`);
    }

    case 'dashcaddy_container_action': {
      const { containerId, action } = args;
      const method = action === 'remove' ? 'DELETE' : 'POST';
      return apiCall(method, `/containers/${containerId}/${action}`);
    }

    // ── Catalog & Discovery ──
    case 'dashcaddy_search_catalog': {
      let path = '/catalog';
      if (args.query) {
        return apiCall('GET', `/catalog/search?q=${encodeURIComponent(args.query)}`);
      }
      if (args.category) path += `?category=${args.category}`;
      return apiCall('GET', path);
    }

    case 'dashcaddy_discover_services': {
      return apiCall('GET', '/discover');
    }

    // ── Deployment ──
    case 'dashcaddy_deploy_app': {
      // Step 1: Get template details
      const template = await apiCall('GET', `/catalog/${args.templateId}`);
      if (template.error) return template;

      // Step 2: Generate Caddyfile route
      const port = args.port || template.ports?.[0] || 8080;
      const subdomain = args.subdomain || args.templateId;
      const caddy = await apiCall('POST', '/caddycode/generate', {
        domain: `${subdomain}.sami`,
        upstream: `localhost:${port}`,
        websocket: true,
        cors: true,
      });

      // Step 3: Create service entry
      const service = await apiCall('POST', '/services', {
        id: subdomain,
        name: template.name,
        subdomain,
        domain: `${subdomain}.sami`,
        url: `https://${subdomain}.sami`,
        port,
        protocol: 'http',
        type: template.category || 'generic',
      });

      return {
        deployed: !service.error,
        service: service.error ? null : service,
        caddyfile: caddy.error ? null : caddy.caddyfile,
        url: `https://${subdomain}.sami`,
        message: service.error
          ? `Deployment failed: ${service.message}`
          : `${template.name} deployed! Access it at https://${subdomain}.sami`,
        nextSteps: [
          `Pull the Docker image: docker pull ${template.image || 'unknown'}`,
          `Run the container with port ${port} mapped`,
          `The Caddyfile route is configured — the URL should work once the container is running`,
        ],
      };
    }

    case 'dashcaddy_wizard_recommend': {
      return apiCall('POST', '/wizard/recommend', {
        categories: args.categories,
        hardwareProfile: args.hardwareProfile || 'medium',
      });
    }

    // ── DNS & Proxy ──
    case 'dashcaddy_list_dns': {
      let path = '/dns';
      if (args.zone) path += `?zone=${args.zone}`;
      return apiCall('GET', path);
    }

    case 'dashcaddy_generate_caddyfile': {
      return apiCall('POST', '/caddycode/generate', {
        domain: args.domain,
        upstream: args.upstream,
        websocket: args.websocket,
        cors: args.cors,
        auth: args.auth,
      });
    }

    // ── Diagnostics ──
    case 'dashcaddy_diagnose': {
      const findings = [];

      if (args.serviceId) {
        // Service-specific diagnosis
        const health = await apiCall('GET', `/services/${args.serviceId}/health`);
        if (health.error) {
          findings.push({ severity: 'critical', message: `Cannot reach service: ${health.message}` });
        } else {
          findings.push({ severity: 'info', message: `Service ${args.serviceId} health: ${JSON.stringify(health)}` });
        }
      }

      // System-wide checks
      const sysHealth = await apiCall('GET', '/system/health');
      if (!sysHealth.error) {
        findings.push({ severity: sysHealth.status === 'healthy' ? 'ok' : 'warning',
          message: `System status: ${sysHealth.status}, services: ${JSON.stringify(sysHealth.checks?.services)}` });

        if (sysHealth.checks?.memory?.percentage > 85) {
          findings.push({ severity: 'warning', message: `High memory usage: ${sysHealth.checks.memory.percentage}%` });
        }
      }

      return { findings, depth: args.depth || 'standard' };
    }

    // ── Backup & Recovery ──
    case 'dashcaddy_create_backup': {
      return apiCall('POST', '/disaster/backup');
    }

    case 'dashcaddy_get_backup_status': {
      return apiCall('GET', '/disaster/status');
    }

    // ── Fleet ──
    case 'dashcaddy_list_fleet': {
      return apiCall('GET', '/fleet/hosts');
    }

    default:
      return { error: true, message: `Unknown tool: ${name}` };
  }
}

// ─── MCP Protocol Handler ───────────────────────────────────────────────────

function handleMessage(msg) {
  const { id, method, params } = msg;

  switch (method) {
    case 'initialize': {
      return {
        jsonrpc: '2.0',
        id,
        result: {
          protocolVersion: MCP_VERSION,
          serverInfo: {
            name: 'dashcaddy',
            version: '1.15.0',
          },
          capabilities: {
            tools: { listChanged: false },
            resources: { listChanged: false, subscribe: false },
          },
        },
      };
    }

    case 'tools/list': {
      return {
        jsonrpc: '2.0',
        id,
        result: { tools: TOOLS },
      };
    }

    case 'tools/call': {
      const { name, arguments: args } = params;
      return handleTool(name, args).then(result => ({
        jsonrpc: '2.0',
        id,
        result: {
          content: [{
            type: 'text',
            text: JSON.stringify(result, null, 2),
          }],
        },
      })).catch(err => ({
        jsonrpc: '2.0',
        id,
        error: { code: -32603, message: err.message },
      }));
    }

    case 'resources/list': {
      return {
        jsonrpc: '2.0',
        id,
        result: {
          resources: [
            { uri: 'dashcaddy://services', name: 'Services', description: 'All DashCaddy services' },
            { uri: 'dashcaddy://health', name: 'System Health', description: 'Current system health status' },
            { uri: 'dashcaddy://catalog', name: 'App Catalog', description: 'Available self-hostable apps' },
          ],
        },
      };
    }

    case 'ping': {
      return { jsonrpc: '2.0', id, result: {} };
    }

    default: {
      if (id) {
        return {
          jsonrpc: '2.0',
          id,
          error: { code: -32601, message: `Method not found: ${method}` },
        };
      }
      // Notification — no response needed
      return null;
    }
  }
}

// ─── Stdio Transport ────────────────────────────────────────────────────────

const rl = readline.createInterface({ input: process.stdin, terminal: false });

process.stderr.write(`[DashCaddy MCP] Server starting — connecting to ${BASE_URL}\n`);

rl.on('line', (line) => {
  if (!line.trim()) return;

  let msg;
  try {
    msg = JSON.parse(line);
  } catch {
    process.stderr.write(`[DashCaddy MCP] Invalid JSON: ${line.substring(0, 100)}\n`);
    return;
  }

  const response = handleMessage(msg);

  if (response && typeof response.then === 'function') {
    // Async handler
    response.then(res => {
      if (res) process.stdout.write(JSON.stringify(res) + '\n');
    }).catch(err => {
      process.stderr.write(`[DashCaddy MCP] Error: ${err.message}\n`);
    });
  } else if (response) {
    // Sync handler
    process.stdout.write(JSON.stringify(response) + '\n');
  }
  // Notifications (no id) get no response
});

rl.on('close', () => {
  process.stderr.write('[DashCaddy MCP] Server shutting down\n');
  process.exit(0);
});
