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 { spawn } from "child_process"
import path from "path"
import { fileURLToPath } from "url"

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const DEFAULT_TIMEOUT = 120000

const ValidateToolInputJsonSchema = {
  type: "object",
  additionalProperties: false,
  required: ["plugin_path"],
  properties: {
    plugin_path: { 
      type: "string", 
      description: "Absolute or relative path to the WordPress plugin directory to validate" 
    },
    verbose: { 
      type: "boolean", 
      description: "Include full script output in addition to structured results (default: false)" 
    },
  },
}

const server = new Server(
  {
    name: "wordpress-validator",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  },
)

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "validate_wordpress_plugin",
        description: `Validates a WordPress plugin for security vulnerabilities, coding standards violations, and common runtime errors. Runs comprehensive static analysis including:
- Forbidden/dangerous function detection
- SQL injection pattern detection  
- XSS and input sanitization checks
- Nonce and capability verification
- PHP syntax validation
- Duplicate class/function detection
- Class loading validation
- File path security checks
- WordPress deprecated function detection

CRITICAL: This tool MUST be called before completing ANY WordPress plugin work. Do NOT mark work complete until validation passes.

Returns structured results with severity categorization. Use verbose=true only if you need full output.`,
        inputSchema: ValidateToolInputJsonSchema,
      },
    ],
  }
})

server.setRequestHandler(CallToolRequestSchema, async (req) => {
  const toolName = req.params.name
  const args = (req.params.arguments ?? {})

  if (toolName === "validate_wordpress_plugin") {
    const parsed = z
      .object({
        plugin_path: z.string().min(1),
        verbose: z.boolean().optional().default(false),
      })
      .safeParse(args && typeof args === "object" ? args : {})

    if (!parsed.success) {
      return {
        content: [{ 
          type: "text", 
          text: JSON.stringify({ 
            passed: false, 
            errorCount: 1,
            warningCount: 0,
            summary: `Validation failed: ${parsed.error.message}`,
            issues: [{ severity: "error", category: "input", message: parsed.error.message }]
          }, null, 2) 
        }],
        isError: true,
      }
    }

    const pluginPath = parsed.data.plugin_path
    const verbose = parsed.data.verbose || false
    const resolvedPath = path.isAbsolute(pluginPath) 
      ? pluginPath 
      : path.resolve(process.cwd(), pluginPath)

    // Check if directory exists
    try {
      const fs = await import("fs/promises")
      const stat = await fs.stat(resolvedPath)
      if (!stat.isDirectory()) {
        return {
          content: [{ 
            type: "text", 
            text: JSON.stringify({ 
              passed: false, 
              errorCount: 1,
              warningCount: 0,
              summary: `Validation failed: Not a directory: ${resolvedPath}`,
              issues: [{ severity: "error", category: "path", message: "Plugin path must be a directory" }]
            }, null, 2) 
          }],
          isError: true,
        }
      }
    } catch (err) {
      return {
        content: [{ 
          type: "text", 
          text: JSON.stringify({ 
            passed: false, 
            errorCount: 1,
            warningCount: 0,
            summary: `Validation failed: Directory not found: ${resolvedPath}`,
            issues: [{ severity: "error", category: "path", message: `Plugin directory does not exist: ${resolvedPath}` }]
          }, null, 2) 
        }],
        isError: true,
      }
    }

    // Find the validation script
    const scriptDir = path.resolve(__dirname, "../../../../scripts")
    const bashScript = path.join(scriptDir, "validate-wordpress-plugin.sh")

    // Check if script exists
    try {
      const fs = await import("fs/promises")
      await fs.access(bashScript)
    } catch {
      return {
        content: [{ 
          type: "text", 
          text: JSON.stringify({ 
            passed: false, 
            errorCount: 1,
            warningCount: 0,
            summary: "Validation failed: Validation script not found",
            issues: [{ severity: "error", category: "setup", message: `Validation script not found: ${bashScript}` }]
          }, null, 2) 
        }],
        isError: true,
      }
    }

    // Run the validation script
    const output = await runValidationScript(bashScript, resolvedPath, DEFAULT_TIMEOUT)

    // Parse the validation output
    const result = parseValidationOutput(output)

    // Build concise summary
    const summaryParts = []
    
    if (result.errorCount > 0) {
      summaryParts.push(`${result.errorCount} errors`)
      result.passed = false
    }
    
    if (result.warningCount > 0) {
      summaryParts.push(`${result.warningCount} warnings`)
    }
    
    if (result.errorCount === 0 && result.warningCount === 0) {
      summaryParts.push("SUCCESS")
      result.passed = true
    }
    
    result.summary = result.passed 
      ? "All validation checks passed"
      : `Validation failed: ${summaryParts.join(", ")}`

    // Build output string
    let outputText = `<validation_result>\n`
    outputText += `<summary>${result.summary}</summary>\n\n`
    
    const errors = result.issues.filter(i => i.severity === "error")
    const warnings = result.issues.filter(i => i.severity === "warning")
    
    if (errors.length > 0) {
      outputText += `<errors count="${errors.length}">\n`
      errors.slice(0, 10).forEach(issue => {
        outputText += `  [${issue.category}] ${issue.file ? `${issue.file}:${issue.line} - ` : ""}${issue.message}\n`
      })
      if (errors.length > 10) {
        outputText += `  ... and ${errors.length - 10} more errors\n`
      }
      outputText += `</errors>\n\n`
    }
    
    if (warnings.length > 0) {
      outputText += `<warnings count="${warnings.length}">\n`
      warnings.slice(0, 5).forEach(issue => {
        outputText += `  [${issue.category}] ${issue.file ? `${issue.file}:${issue.line} - ` : ""}${issue.message}\n`
      })
      if (warnings.length > 5) {
        outputText += `  ... and ${warnings.length - 5} more warnings\n`
      }
      outputText += `</warnings>\n`
    }
    
    outputText += `</validation_result>`

    if (verbose) {
      outputText += `\n\n<raw_output>\n${output.slice(-3000)}\n</raw_output>`
    }

    return {
      content: [{ type: "text", text: outputText }],
    }
  }

  return {
    content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
    isError: true,
  }
})

async function runValidationScript(scriptPath, pluginPath, timeout) {
  return new Promise((resolve, reject) => {
    const proc = spawn("bash", [scriptPath, pluginPath], {
      stdio: ["ignore", "pipe", "pipe"],
      detached: process.platform !== "win32",
    })

    let output = ""
    proc.stdout?.on("data", (chunk) => { output += chunk.toString() })
    proc.stderr?.on("data", (chunk) => { output += chunk.toString() })

    let timedOut = false
    const timeoutTimer = setTimeout(() => {
      timedOut = true
      proc.kill("SIGTERM")
      setTimeout(() => proc.kill("SIGKILL"), 5000)
    }, timeout)

    proc.on("exit", () => {
      clearTimeout(timeoutTimer)
      if (timedOut) {
        output += "\n\n[Validation timed out after " + timeout + "ms]"
      }
      resolve(output)
    })

    proc.on("error", (err) => {
      clearTimeout(timeoutTimer)
      reject(err)
    })
  })
}

function parseValidationOutput(output) {
  const result = {
    passed: true,
    errorCount: 0,
    warningCount: 0,
    issues: [],
    summary: "",
  }
  
  const lines = output.split("\n")
  
  for (const line of lines) {
    const trimmed = line.trim()
    
    if (trimmed.includes("✗") || trimmed.includes("FATAL") || trimmed.includes("ERROR")) {
      const issue = parseIssueLine(trimmed, "error")
      if (issue && !isDuplicateIssue(result.issues, issue)) {
        result.issues.push(issue)
        result.errorCount++
      }
    }
    
    if (trimmed.includes("⚠") || trimmed.includes("WARNING")) {
      const issue = parseIssueLine(trimmed, "warning")
      if (issue && !isDuplicateIssue(result.issues, issue)) {
        result.issues.push(issue)
        result.warningCount++
      }
    }
  }
  
  return result
}

function parseIssueLine(line, severity) {
  const cleanLine = line.replace(/\x1b\[[0-9;]*m/g, "")
  
  const fileMatch = cleanLine.match(/([\w\/\\.-]+\.php):(\d+)/)
  const file = fileMatch ? fileMatch[1] : undefined
  const lineNum = fileMatch ? parseInt(fileMatch[2], 10) : undefined
  
  const categoryMatch = cleanLine.match(/\[\d+\/\d+\]\s+Checking\s+(?:for\s+)?(.+?)\.\.\./i)
  const category = categoryMatch ? categoryMatch[1] : extractCategory(cleanLine)
  
  let message = cleanLine
    .replace(/^\s*[✗⚠]\s*/, "")
    .replace(/^\s*FATAL:\s*/i, "")
    .replace(/^\s*ERROR:\s*/i, "")
    .replace(/^\s*WARNING:\s*/i, "")
    .replace(/^\s*SECURITY\s+RISK\s+in\s+/, "")
    .replace(/^\s*SQL\s+INJECTION\s+RISK\s+in\s+/, "")
    .replace(/^\s*Found\s+/, "")
    .trim()
  
  if (!message || message.length < 10) {
    return null
  }
  
  return {
    severity,
    category,
    file,
    line: lineNum,
    message: message.substring(0, 200),
  }
}

function extractCategory(line) {
  if (line.includes("forbidden") || line.includes("eval") || line.includes("exec")) {
    return "security"
  }
  if (line.includes("SQL") || line.includes("wpdb")) {
    return "sql-injection"
  }
  if (line.includes("XSS") || line.includes("sanitize") || line.includes("escape")) {
    return "xss"
  }
  if (line.includes("syntax") || line.includes("parse")) {
    return "syntax"
  }
  if (line.includes("duplicate") || line.includes("redeclare")) {
    return "duplicates"
  }
  if (line.includes("missing") || line.includes("undefined")) {
    return "undefined"
  }
  if (line.includes("class") || line.includes("function")) {
    return "structure"
  }
  return "general"
}

function isDuplicateIssue(issues, newIssue) {
  return issues.some(i => 
    i.message === newIssue.message && 
    i.file === newIssue.file && 
    i.line === newIssue.line
  )
}

const transport = new StdioServerTransport()
await server.connect(transport)
