#!/usr/bin/env node

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'
import type { RollerRollResult } from '@randsum/roller'
import {
  type DiceNotation,
  type ValidationResult,
  isDiceNotation,
  roll,
  validateNotation
} from '@randsum/roller'
import { createServer } from 'http'
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
import { Command } from 'commander'
import { readFileSync } from 'fs'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'

const program = new Command()
  .option('--transport <type>', 'transport type', 'stdio')
  .option('--port <number>', 'port for HTTP/SSE transport', '3000')
  .allowUnknownOption() // Passthrough for other services
  .parse(process.argv)

const cliOptions = program.opts<{
  transport: string
  port: string
}>()

const allowedTransports = ['stdio', 'http', 'sse']
if (!allowedTransports.includes(cliOptions.transport)) {
  console.error(
    `Invalid --transport value: '${cliOptions.transport}'. Must be one of: stdio, http, sse.`
  )
  process.exit(1)
}

const TRANSPORT_TYPE = (cliOptions.transport || 'stdio') as 'stdio' | 'http' | 'sse'

const CLI_PORT = (() => {
  const parsed = parseInt(cliOptions.port, 10)
  return isNaN(parsed) ? undefined : parsed
})()

const sseTransports: Record<string, SSEServerTransport> = {}

const diceNotationSchema = z
  .string()
  .min(1, 'Dice notation cannot be empty')
  .refine((value): value is DiceNotation => isDiceNotation(value), {
    message: 'Invalid dice notation. Use formats like "2d20+5", "4d6L", "3d8!"'
  })

const rollToolSchema = z.object({
  notation: diceNotationSchema.describe(
    `RANDSUM dice notation string following pattern: {quantity}d{sides}{modifiers}

BASIC EXAMPLES:
• "2d6" - Roll 2 six-sided dice
• "1d20+5" - Roll 1d20, add 5
• "4d6L" - Roll 4d6, drop lowest (D&D ability score)
• "2d20H" - Roll 2d20, keep highest (advantage)
• "3d6!" - Roll 3d6 with exploding dice
• "4d6R{1}" - Roll 4d6, reroll any 1s
• "4d20U" - Roll 4d20 with unique results
• "4d20C{>18}" - Roll 4d20, cap results above 18

COMPLEX COMBINATIONS:
• "4d6LR{1}+3" - Drop lowest, reroll 1s, add 3
• "4d6LHR{1,6}C{<2,>5}U!+10-3" - All modifiers combined

GAMING PATTERNS:
• D&D Ability: "4d6L"
• D&D Advantage: "2d20H"
• D&D Damage: "1d8+3"
• Skill Check: "1d20+7"`
  )
})

const validateNotationToolSchema = z.object({
  notation: z.string().min(1, 'Notation string cannot be empty')
    .describe(`Dice notation string to validate and parse. Can be any potential RANDSUM notation.

VALIDATION EXAMPLES:
• "4d6L" - Valid drop lowest syntax
• "2d20H" - Valid advantage mechanics
• "3d6!" - Valid exploding dice
• "4d6R{1,2}" - Valid reroll syntax
• "4d6R{<=3}" - Invalid (unsupported operator)
• "1d4+1d6" - Invalid (multiple expressions)

USE CASES:
• Validate user input before rolling
• Learn notation syntax and structure
• Debug complex modifier combinations
• Understand parsing behavior
• Catch common notation errors

RETURNS: For valid notation, shows parsed structure with quantity, sides, and modifiers. For invalid notation, provides specific error with correction guidance.`)
})

function formatRollResult(result: RollerRollResult): string {
  const {
    total,
    rolls: [
      {
        parameters,
        modifierHistory: { modifiedRolls, initialRolls }
      }
    ]
  } = result

  const header = `🎲 RANDSUM Roll Result:`
  const separator = '─'.repeat(30)
  const totalLine = `Total: ${total}`

  const notation = parameters.notation
  const rawRollsStr = initialRolls.join(', ')
  const modifiedRollsStr = modifiedRolls.join(', ')

  let rollInfo = `Roll: ${notation}`
  rollInfo += `\n  Raw: [${rawRollsStr}]`

  if (rawRollsStr !== modifiedRollsStr) {
    rollInfo += `\n  Modified: [${modifiedRollsStr}]`
  }

  rollInfo += `\n  Total: ${total}`

  const rawResultsLine = `Raw Results: [${initialRolls.join(', ')}]`

  return [header, separator, totalLine, rawResultsLine, '', 'Roll Details:', rollInfo].join('\n')
}

function formatValidationResult(result: ValidationResult): string {
  if (!result.valid) {
    return `❌ Invalid Dice Notation`
  }

  const { notation, description, options } = result
  const header = `✅ Valid Dice Notation:`
  const separator = '─'.repeat(25)

  const details = [
    `Notation: ${notation}`,
    `Description: ${description.join(', ')}`,
    '',
    'Parsed Details:',
    JSON.stringify(options, null, 2)
  ]

  return [header, separator, ...details].join('\n')
}

// Read version from package.json at runtime
function getPackageVersion(): string {
  try {
    const __filename = fileURLToPath(import.meta.url)
    const __dirname = dirname(__filename)
    // In built form, we're in dist/ and package.json is in parent directory
    const packageJsonPath = join(__dirname, '..', 'package.json')
    const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
      version: string
    }
    return packageJson.version
  } catch (error) {
    console.error('Failed to read package version:', error)
    return '0.0.0' // Fallback version
  }
}

export const config: { version: string; name: string } = {
  version: getPackageVersion(),
  name: 'RANDSUM'
}

function createServerInstance(): McpServer {
  const server = new McpServer(config, {
    instructions: `🎲 RANDSUM MCP Server - Advanced Dice Rolling & Game Mechanics Engine

COMPREHENSIVE DICE ROLLING SYSTEM with sophisticated modifiers for tabletop gaming, probability simulation, and randomization tasks.

🚀 CORE CAPABILITIES:
• Execute dice rolls with advanced RANDSUM notation system
• Support for standard polyhedral dice (d4, d6, d8, d10, d12, d20, d100, etc.)
• Complex modifier combinations for sophisticated game mechanics
• Detailed roll breakdowns with individual die results and modifier applications
• Real-time notation validation with comprehensive error feedback

🔧 AVAILABLE TOOLS:

🎯 roll - Advanced Dice Rolling Engine
• Execute sophisticated dice rolls with full modifier support
• Returns detailed breakdowns: total, raw results, modified results, subtotals
• Supports numeric dice (standard gaming)
• Handles complex modifier combinations for advanced game mechanics

🔍 validate-notation - Syntax Validator & Parser
• Validate dice notation before execution to prevent errors
• Detailed parsing feedback showing notation interpretation
• Comprehensive error messages with correction guidance
• Essential for learning syntax and debugging complex expressions

📚 COMPREHENSIVE MODIFIER SYSTEM:

DROP MODIFIERS (L/H): Remove extreme results
• 4d6L - Drop lowest (D&D ability scores)
• 2d20H - Drop highest (disadvantage mechanics)
• 4d6LH - Drop both extremes (middle values)

REROLL MODIFIERS (R): Conditional rerolling
• 4d6R{1} - Reroll 1s (avoid failures)
• 4d6R{<3} - Reroll below threshold
• 4d6R{1,2,6} - Reroll specific values

EXPLODING DICE (!): Cascade rolling
• 3d6! - Reroll and add on maximum (critical hits)
• Open-ended results for dramatic outcomes

UNIQUE RESULTS (U): No duplicates
• 4d20U - All different results (card draws, selections)

CAPPING (C): Range enforcement
• 4d20C{>18} - Cap maximum values
• 4d6C{<2,>5} - Enforce result ranges

ARITHMETIC (+/-): Fixed adjustments
• 2d6+3 - Add modifiers (damage + ability)
• 1d20-2 - Apply penalties

🎮 GAMING APPLICATIONS:
• D&D/Pathfinder: Ability scores, attacks, damage, saves
• Narrative Games: Story prompts, oracle dice, complications
• Probability: Statistical analysis, random sampling
• Custom Systems: Unique mechanics, symbol resolution

� LLM INTEGRATION BEST PRACTICES:
• Always validate complex notation before rolling
• Use for character creation, combat resolution, skill challenges
• Combine modifiers for sophisticated game mechanics

📖 COMPLETE REFERENCE:
Access via dice-notation-docs resource for comprehensive syntax guide

🔥 QUICK EXAMPLES:
• 4d6L - D&D ability score (drop lowest)
• 2d20H - Advantage roll (keep highest)
• 3d6! - Exploding damage dice
• 4d6R{1}+3 - Reroll 1s, add modifier
• 1d100+10 - Percentile roll with modifier`
  })

  server.tool(
    'roll',
    `🎲 RANDSUM Advanced Dice Rolling Engine

COMPREHENSIVE DICE ROLLING with sophisticated modifier support for tabletop gaming, probability simulation, and randomization tasks.

📋 CORE FUNCTIONALITY:
• Execute dice rolls using advanced RANDSUM notation
• Returns detailed breakdowns with individual die results, modifier applications, and final totals
• Supports both numeric dice (standard polyhedral)
• Handles complex modifier combinations for sophisticated game mechanics

🔧 SUPPORTED NOTATION PATTERNS:

BASIC DICE:
• 2d6, 1d20, 4d8, 100d1 - Standard polyhedral dice
• 0d6, 2d0 - Edge cases (valid but produce no/zero results)

DROP MODIFIERS (L/H) - Remove extreme results:
• 4d6L - Roll 4d6, drop lowest (D&D ability scores)
• 4d6L2 - Roll 4d6, drop 2 lowest
• 2d20H - Roll 2d20, drop highest (disadvantage)
• 4d6LH - Drop both lowest and highest (middle values only)

REROLL MODIFIERS (R) - Conditional rerolling:
• 4d6R{1} - Reroll any 1s (avoid critical failures)
• 4d6R{1,2} - Reroll 1s and 2s (higher minimum)
• 4d6R{<3} - Reroll results below 3
• 4d6R{>4} - Reroll results above 4
• 4d6R{<2,>5} - Reroll results outside 2-5 range

EXPLODING DICE (!) - Cascade rolling:
• 3d6! - Roll 3d6, reroll and add on 6s (critical hits)
• 2d10! - Roll 2d10, reroll and add on 10s (open-ended results)

UNIQUE RESULTS (U) - No duplicates:
• 4d20U - Roll 4d20 with all different results (card draws)
• 5d6U - Roll 5d6 ensuring no duplicates (lottery systems)

CAPPING MODIFIERS (C) - Range enforcement:
• 4d20C{>18} - No results above 18 (cap maximum)
• 4d20C{<3} - No results below 3 (cap minimum)
• 4d6C{<2,>5} - Results must be 2-5 only (bounded results)

ARITHMETIC MODIFIERS (+/-) - Fixed adjustments:
• 2d6+3 - Roll 2d6, add 3 (damage + modifier)
• 1d20+5 - Roll 1d20, add 5 (skill check with bonus)
• 2d6+10-3 - Multiple operations (net +7)

COMPLEX COMBINATIONS:
• 4d6LR{1}!+3 - Drop lowest, reroll 1s, exploding, +3
• 4d6LHR{1,6}C{<2,>5}U!+10-3 - All modifiers combined

🎮 COMMON GAMING PATTERNS:
• D&D Ability Scores: 4d6L
• D&D Advantage: 2d20H
• D&D Disadvantage: 2d20L
• Damage + Modifier: 1d8+3
• Critical Hit: 2d8+3
• Skill Check: 1d20+{modifier}

🚫 UNSUPPORTED FEATURES:
• Multiplication/Division: 2d6*2, 2d6/2
• Compound operators: <=, >=
• Multiple expressions: 1d4+1d6+2d8
• Keep highest/lowest notation (use drop instead)

💡 LLM USAGE TIPS:
• Always validate complex notation with validate-notation tool first
• Use for character creation, combat resolution, skill challenges, random generation
• Combine modifiers for sophisticated game mechanics

RETURNS: Detailed breakdown with total, raw results, modified results (if different), and subtotal for each roll group.`,
    rollToolSchema.shape,
    ({ notation }) => {
      try {
        const result = roll(notation)
        return {
          content: [
            {
              type: 'text',
              text: formatRollResult(result)
            }
          ]
        }
      } catch (error: unknown) {
        return {
          content: [
            {
              type: 'text',
              text: `❌ Error rolling dice: ${error instanceof Error ? error.message : error}`
            }
          ]
        }
      }
    }
  )

  server.tool(
    'validate-notation',
    `🔍 RANDSUM Notation Syntax Validator & Parser

COMPREHENSIVE VALIDATION ENGINE for dice notation syntax with detailed parsing feedback and error guidance.

📋 CORE FUNCTIONALITY:
• Validate dice notation syntax before execution
• Provide detailed parsing breakdown showing how notation will be interpreted
• Generate specific error messages with correction guidance
• Essential for learning notation syntax and debugging complex expressions

🎯 WHEN TO USE:
• Before executing complex modifier combinations
• When processing user-provided notation input
• For learning and understanding notation syntax
• When debugging unexpected roll behavior
• Before building complex dice expressions

🔧 VALIDATION FEATURES:

SYNTAX CHECKING:
• Verifies proper dice notation format (NdS pattern)
• Validates modifier syntax and combinations
• Ensures logical parameter values

DETAILED PARSING:
• Shows parsed structure: quantity, sides, modifiers
• Breaks down modifier parameters and conditions
• Explains how each component will be processed
• Displays final notation interpretation

ERROR GUIDANCE:
• Specific error messages for common mistakes
• Suggestions for correcting invalid notation
• Explains why certain combinations are invalid
• Provides alternative notation patterns

📊 RETURN FORMATS:

VALID NOTATION:
✅ Valid Dice Notation (numeric):
• Notation: [input notation]
• Description: [human-readable explanation]
• Parsed Details: [JSON structure showing components]

INVALID NOTATION:
❌ Invalid Dice Notation
• Error: [specific error description]
• Common causes and solutions

🎮 COMMON VALIDATION SCENARIOS:

LEARNING PATTERNS:
• validate-notation("4d6L") → Understand drop lowest syntax
• validate-notation("2d20H") → Learn advantage mechanics
• validate-notation("3d6!") → Explore exploding dice

DEBUGGING COMPLEX NOTATION:
• validate-notation("4d6LR{1}!+3") → Verify modifier combination
• validate-notation("4d6C{<2,>5}") → Check capping syntax

ERROR PREVENTION:
• validate-notation("4d6R{<=3}") → Identify unsupported operators
• validate-notation("1d4+1d6") → Catch multiple expression errors
• validate-notation("2d6*2") → Find unsupported arithmetic

💡 LLM INTEGRATION PATTERNS:

VALIDATION-FIRST WORKFLOW:
1. validate-notation(user_input) → Check syntax
2. If valid → roll(user_input) → Execute roll
3. If invalid → Provide corrected notation

LEARNING ASSISTANCE:
1. validate-notation(complex_notation) → Show parsing
2. Explain each modifier component
3. Suggest simpler alternatives if needed

BATCH VALIDATION:
• Validate multiple notation patterns for comparison
• Build complex expressions step by step
• Verify each modifier addition before combining

🚨 COMMON ERROR PATTERNS TO CATCH:
• Compound operators: "4d6R{<=3}" → Use "<4" instead
• Multiple expressions: "1d4+1d6" → Single expression only
• Unsupported arithmetic: "2d6*2" → Use multiple rolls
• Invalid conditions: "4d6C{3,4,5}" → Use range conditions

RETURNS: For valid notation, returns parsed structure with quantity, sides, and modifier details. For invalid notation, returns specific error message with correction guidance.`,
    validateNotationToolSchema.shape,
    ({ notation }) => {
      try {
        const validation = validateNotation(notation)
        return {
          content: [
            {
              type: 'text',
              text: formatValidationResult(validation)
            }
          ]
        }
      } catch (error: unknown) {
        return {
          content: [
            {
              type: 'text',
              text: `❌ Error validating notation: ${error instanceof Error ? error.message : error}`
            }
          ]
        }
      }
    }
  )

  return server
}

async function main(): Promise<void> {
  try {
    const shutdown = (): void => {
      console.error('Shutting down RANDSUM MCP server...')
      process.exit(0)
    }

    process.on('SIGINT', shutdown)
    process.on('SIGTERM', shutdown)

    switch (TRANSPORT_TYPE) {
      case 'stdio':
        await runStdioTransport()
        break
      case 'http': {
        const httpPort = CLI_PORT ?? 3000
        runHttpTransport(httpPort)
        break
      }
      case 'sse': {
        const ssePort = CLI_PORT ?? 3000
        runSseTransport(ssePort)
        break
      }
      default:
        throw new Error(`Unsupported transport: ${TRANSPORT_TYPE as string}`)
    }
  } catch (error: unknown) {
    console.error('Fatal error starting RANDSUM MCP server:', error)
    process.exit(1)
  }
}

async function runStdioTransport(): Promise<void> {
  const server = createServerInstance()
  const transport = new StdioServerTransport()
  await server.connect(transport)
}

function runHttpTransport(port: number): void {
  const server = createServerInstance()
  const httpServer = createServer()

  httpServer.on('request', (req, res) => {
    // Enable CORS
    res.setHeader('Access-Control-Allow-Origin', '*')
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')

    if (req.method === 'OPTIONS') {
      res.writeHead(200)
      res.end()
      return
    }

    const transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: () => Math.random().toString(36).substring(2, 15)
    })
    server.connect(transport).catch((error: unknown) => {
      console.error('HTTP transport error:', error)
      if (!res.headersSent) {
        res.writeHead(500)
        res.end('Internal Server Error')
      }
    })
  })

  httpServer.listen(port, () => {
    console.error(`RANDSUM MCP server running on http://localhost:${port}`)
  })
}

function runSseTransport(port: number): void {
  const httpServer = createServer()

  httpServer.on('request', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*')
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')

    if (req.method === 'OPTIONS') {
      res.writeHead(200)
      res.end()
      return
    }

    if (req.url?.startsWith('/sse')) {
      const url = new URL(req.url, `http://${req.headers.host}`)
      const sessionId = url.searchParams.get('sessionId')

      if (!sessionId) {
        res.writeHead(400)
        res.end('Missing sessionId parameter')
        return
      }

      const server = createServerInstance()
      const transport = new SSEServerTransport('/sse', res)
      sseTransports[sessionId] = transport

      server.connect(transport).catch((error: unknown) => {
        console.error('SSE transport error:', error)
        // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
        delete sseTransports[sessionId]
      })

      req.on('close', () => {
        // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
        delete sseTransports[sessionId]
      })
    } else {
      res.writeHead(404)
      res.end('Not Found')
    }
  })

  httpServer.listen(port, () => {
    console.error(`RANDSUM MCP server (SSE) running on http://localhost:${port}`)
  })
}

main().catch((error: unknown) => {
  console.error('Unhandled error:', error)
  process.exit(1)
})
