#!/usr/bin/env node

/**
 * ✅ CI-specific MCP Inspector Testing (TypeScript)
 * Based on patterns from @modelcontextprotocol/inspector/cli/scripts/cli-tests.js
 * Adapted for Legal MCP Server continuous integration
 */

import { spawn, type ChildProcess } from 'child_process';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { getLocalMcpServerRuntime } from '../helpers/local-mcp-runtime.ts';

const __dirname = dirname(fileURLToPath(import.meta.url));
const projectRoot = join(__dirname, '../..');
const localServerRuntime = getLocalMcpServerRuntime(projectRoot);
const REMOTE_BEARER_TOKEN =
  process.env.MCP_REMOTE_BEARER_TOKEN?.trim() ||
  process.env.REMOTE_SERVER_BEARER_TOKEN?.trim() ||
  '';

// Configuration
interface Config {
  server: {
    command: string;
    args: string[];
    env: Record<string, string>;
  };
  remoteServer: {
    url: string;
    transport: string;
  };
  timeout: number;
  outputDir: string;
  extended: boolean;
}

const CONFIG: Config = {
  server: {
    command: localServerRuntime.command,
    args: localServerRuntime.args,
    env: { NODE_ENV: 'test' },
  },
  remoteServer: {
    url: process.env.REMOTE_SERVER_URL || 'https://courtlistenermcp.blakeoxford.com/mcp',
    transport: 'streamable-http',
  },
  timeout: 45000, // 45 seconds
  outputDir: join(projectRoot, 'test-output'),
  extended: process.argv.includes('--extended'),
};

// Colors for output
const colors = {
  GREEN: '\x1b[32m',
  YELLOW: '\x1b[33m',
  RED: '\x1b[31m',
  BLUE: '\x1b[34m',
  NC: '\x1b[0m', // No Color
};

// Test results tracking
let totalTests = 0;
let passedTests = 0;
let failedTests = 0;

/**
 * Test case interface
 */
interface TestCase {
  name: string;
  args: string[];
  validate: (output: string) => boolean;
  required: boolean;
  allowNonZeroExit?: boolean;
}

/**
 * Test cases for MCP Inspector CLI validation
 */
const testCases: TestCase[] = [
  {
    name: 'MCP Inspector CLI - Tools List',
    args: [
      'npx',
      '@modelcontextprotocol/inspector',
      '--cli',
      localServerRuntime.command,
      ...localServerRuntime.args,
      '--method',
      'tools/list',
    ],
    validate: (output) => {
      return (
        output.includes('search_cases') &&
        output.includes('get_case_details') &&
        output.includes('list_courts')
      );
    },
    required: true,
  },
  {
    name: 'MCP Inspector CLI - Tool Call (List Courts)',
    args: [
      'npx',
      '@modelcontextprotocol/inspector',
      '--cli',
      localServerRuntime.command,
      ...localServerRuntime.args,
      '--method',
      'tools/call',
      '--tool-name',
      'list_courts',
      '--tool-arg',
      'jurisdiction=F',
    ],
    validate: (output) => {
      return output.includes('courts') || output.includes('result');
    },
    required: true,
  },
  {
    name: 'MCP Inspector CLI - Tool Call (Search Cases)',
    args: [
      'npx',
      '@modelcontextprotocol/inspector',
      '--cli',
      localServerRuntime.command,
      ...localServerRuntime.args,
      '--method',
      'tools/call',
      '--tool-name',
      'search_cases',
      '--tool-arg',
      'query=constitutional',
      '--tool-arg',
      'page_size=5',
    ],
    validate: (output) => {
      return output.includes('results') || output.includes('cases');
    },
    required: false, // Optional as it requires external API
  },
];

// Extended test cases (only run with --extended flag)
const extendedTestCases: TestCase[] = [
  {
    name: 'MCP Inspector CLI - All Core Tools',
    args: [
      'npx',
      '@modelcontextprotocol/inspector',
      '--cli',
      localServerRuntime.command,
      ...localServerRuntime.args,
      '--method',
      'tools/list',
    ],
    validate: (output) => {
      const coreTools = [
        'search_cases',
        'get_case_details',
        'get_opinion_text',
        'lookup_citation',
        'get_related_cases',
        'list_courts',
        'analyze_legal_argument',
      ];
      return coreTools.every((tool) => output.includes(tool));
    },
    required: true,
  },
  {
    name: 'MCP Inspector CLI - Advanced Tools',
    args: [
      'npx',
      '@modelcontextprotocol/inspector',
      '--cli',
      localServerRuntime.command,
      ...localServerRuntime.args,
      '--method',
      'tools/list',
    ],
    validate: (output) => {
      const advancedTools = ['get_dockets', 'get_judges', 'get_oral_arguments', 'advanced_search'];
      return advancedTools.some((tool) => output.includes(tool));
    },
    required: true,
  },
  {
    name: 'MCP Inspector CLI - Error Handling',
    args: [
      'npx',
      '@modelcontextprotocol/inspector',
      '--cli',
      localServerRuntime.command,
      ...localServerRuntime.args,
      '--method',
      'tools/call',
      '--tool-name',
      'nonexistent_tool',
    ],
    validate: (output) => {
      return (
        output.toLowerCase().includes('error') ||
        output.toLowerCase().includes('not found') ||
        output.toLowerCase().includes('unknown tool')
      );
    },
    required: true,
    allowNonZeroExit: true,
  },
];

/**
 * Run a single test case
 */
async function runTest(testCase: TestCase): Promise<boolean> {
  totalTests++;
  console.log(`\n${colors.BLUE}🧪 Testing: ${testCase.name}${colors.NC}`);
  console.log(`${colors.BLUE}Command: ${testCase.args.join(' ')}${colors.NC}`);

  return new Promise((resolve) => {
    const childProcess = spawn(testCase.args[0], testCase.args.slice(1), {
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, ...CONFIG.server.env },
    });

    let stdout = '';
    let stderr = '';

    childProcess.stdout?.on('data', (data: Buffer) => {
      stdout += data.toString();
    });

    childProcess.stderr?.on('data', (data: Buffer) => {
      stderr += data.toString();
    });

    const timeout = setTimeout(() => {
      childProcess.kill();
      console.log(`${colors.YELLOW}⚠️  Test timed out: ${testCase.name}${colors.NC}`);
      if (testCase.required) {
        failedTests++;
        resolve(false);
      } else {
        console.log(`${colors.YELLOW}⏭️  Skipping optional test${colors.NC}`);
        resolve(true);
      }
    }, CONFIG.timeout);

    childProcess.on('close', (code: number | null) => {
      clearTimeout(timeout);

      const output = stdout + stderr;
      const logFile = join(CONFIG.outputDir, `${testCase.name.replace(/[^a-zA-Z0-9]/g, '_')}.log`);

      // Save test output
      if (!fs.existsSync(CONFIG.outputDir)) {
        fs.mkdirSync(CONFIG.outputDir, { recursive: true });
      }
      fs.writeFileSync(logFile, output);

      const exitCodeAllowed = code === 0 || testCase.allowNonZeroExit === true;

      if (exitCodeAllowed && testCase.validate(output)) {
        console.log(`${colors.GREEN}✅ PASSED: ${testCase.name}${colors.NC}`);
        passedTests++;
        resolve(true);
      } else {
        if (testCase.required) {
          console.log(`${colors.RED}❌ FAILED: ${testCase.name}${colors.NC}`);
          console.log(`${colors.RED}Exit code: ${code}${colors.NC}`);
          console.log(`${colors.RED}Output: ${output.substring(0, 500)}...${colors.NC}`);
          failedTests++;
          resolve(false);
        } else {
          console.log(`${colors.YELLOW}⏭️  SKIPPED: ${testCase.name} (optional)${colors.NC}`);
          resolve(true);
        }
      }
    });

    childProcess.on('error', (error: Error) => {
      clearTimeout(timeout);
      console.log(`${colors.RED}❌ ERROR: ${testCase.name} - ${error.message}${colors.NC}`);
      if (testCase.required) {
        failedTests++;
        resolve(false);
      } else {
        resolve(true);
      }
    });
  });
}

/**
 * Test remote server with direct HTTP calls
 */
async function testRemoteServer(): Promise<boolean> {
  if (!CONFIG.remoteServer.url) {
    console.log(`${colors.YELLOW}⏭️  Skipping remote server tests (no URL configured)${colors.NC}`);
    return true;
  }
  if (!REMOTE_BEARER_TOKEN && process.env.REMOTE_SERVER_ALLOW_UNAUTH !== 'true') {
    console.log(
      `\n${colors.YELLOW}⏭️  Skipping remote server tests (set MCP_REMOTE_BEARER_TOKEN or REMOTE_SERVER_ALLOW_UNAUTH=true)${colors.NC}`,
    );
    return true;
  }

  console.log(`\n${colors.BLUE}🌐 Testing Remote Server: ${CONFIG.remoteServer.url}${colors.NC}`);

  interface RemoteTest {
    name: string;
    payload: {
      jsonrpc: string;
      id: number;
      method: string;
      params?: Record<string, unknown>;
    };
  }

  const remoteTests: RemoteTest[] = [
    {
      name: 'Remote Server - Initialize',
      payload: {
        jsonrpc: '2.0',
        id: 1,
        method: 'initialize',
        params: {
          protocolVersion: '2024-11-05',
          capabilities: { tools: {} },
          clientInfo: { name: 'CI Test Client', version: '1.0.0' },
        },
      },
    },
    {
      name: 'Remote Server - List Tools',
      payload: {
        jsonrpc: '2.0',
        id: 2,
        method: 'tools/list',
      },
    },
  ];

  for (const test of remoteTests) {
    totalTests++;
    console.log(`\n${colors.BLUE}🧪 Testing: ${test.name}${colors.NC}`);

    try {
      const response = await fetch(CONFIG.remoteServer.url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json, text/event-stream',
          'MCP-Protocol-Version': '2024-11-05',
          ...(REMOTE_BEARER_TOKEN && {
            Authorization: `Bearer ${REMOTE_BEARER_TOKEN}`,
          }),
        },
        body: JSON.stringify(test.payload),
      });

      if (!response.ok) {
        console.log(`${colors.RED}❌ HTTP Error: ${response.status}${colors.NC}`);
        failedTests++;
        continue;
      }

      const result = (await response.json()) as {
        result?: unknown;
        error?: unknown;
      };

      if (result.result || result.error) {
        console.log(`${colors.GREEN}✅ PASSED: ${test.name}${colors.NC}`);
        passedTests++;
      } else {
        console.log(`${colors.RED}❌ FAILED: Invalid response format${colors.NC}`);
        failedTests++;
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.log(`${colors.RED}❌ FAILED: ${test.name} - ${errorMessage}${colors.NC}`);
      failedTests++;
    }
  }

  return failedTests === 0;
}

/**
 * Generate test report
 */
function generateReport(): {
  timestamp: string;
  totalTests: number;
  passedTests: number;
  failedTests: number;
  successRate: string;
  extended: boolean;
} {
  const report = {
    timestamp: new Date().toISOString(),
    totalTests,
    passedTests,
    failedTests,
    successRate: totalTests > 0 ? ((passedTests / totalTests) * 100).toFixed(1) : '0.0',
    extended: CONFIG.extended,
  };

  const reportPath = join(CONFIG.outputDir, 'ci-mcp-inspector-report.json');
  fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));

  console.log(`\n${colors.BLUE}📄 Test report saved: ${reportPath}${colors.NC}`);
  return report;
}

/**
 * Main test runner
 */
async function main(): Promise<void> {
  console.log(`${colors.YELLOW}🚀 Legal MCP Server - CI Inspector Testing${colors.NC}`);
  console.log(`${colors.YELLOW}===========================================${colors.NC}\n`);

  console.log(`Extended mode: ${CONFIG.extended ? 'ON' : 'OFF'}`);
  console.log(`Output directory: ${CONFIG.outputDir}`);
  console.log(`Remote server: ${CONFIG.remoteServer.url}\n`);

  try {
    // Ensure output directory exists
    if (!fs.existsSync(CONFIG.outputDir)) {
      fs.mkdirSync(CONFIG.outputDir, { recursive: true });
    }

    // Run basic test cases
    console.log(`${colors.YELLOW}=== Running Basic MCP Inspector Tests ===${colors.NC}`);
    for (const testCase of testCases) {
      await runTest(testCase);
    }

    // Run extended test cases if requested
    if (CONFIG.extended) {
      console.log(`\n${colors.YELLOW}=== Running Extended MCP Inspector Tests ===${colors.NC}`);
      for (const testCase of extendedTestCases) {
        await runTest(testCase);
      }
    }

    // Test remote server
    await testRemoteServer();

    // Generate final report
    const report = generateReport();

    // Print summary
    console.log(`\n${colors.YELLOW}=== Test Summary ===${colors.NC}`);
    console.log(`Total tests: ${totalTests}`);
    console.log(`${colors.GREEN}Passed: ${passedTests}${colors.NC}`);
    console.log(`${colors.RED}Failed: ${failedTests}${colors.NC}`);
    console.log(`Success rate: ${report.successRate}%`);

    if (failedTests === 0) {
      console.log(`\n${colors.GREEN}🎉 All MCP Inspector tests passed!${colors.NC}`);
      process.exit(0);
    } else {
      // Check success rate - if >= 80%, consider it acceptable
      const successRate = totalTests > 0 ? (passedTests / totalTests) * 100 : 0;
      if (successRate >= 80.0) {
        console.log(
          `\n${colors.YELLOW}⚠️  ${failedTests} test(s) failed, but success rate is ${successRate.toFixed(1)}%${colors.NC}`,
        );
        console.log(`${colors.GREEN}✅ Core MCP Inspector functionality is working${colors.NC}`);
        process.exit(0);
      } else {
        console.log(`\n${colors.RED}❌ ${failedTests} test(s) failed${colors.NC}`);
        process.exit(1);
      }
    }
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    console.error(`${colors.RED}❌ CI test suite failed: ${errorMessage}${colors.NC}`);
    process.exit(1);
  }
}

// Check if this script is being run directly
if (import.meta.url === `file://${process.argv[1]}`) {
  // Check Node.js version (require Node 18+ for fetch)
  const nodeVersionMatch = process.version.match(/^v(\d+)/);
  if (nodeVersionMatch) {
    const nodeVersion = parseInt(nodeVersionMatch[1], 10);
    if (nodeVersion < 18) {
      console.error(
        `${colors.RED}❌ This script requires Node.js 18+ for fetch support${colors.NC}`,
      );
      process.exit(1);
    }
  }

  main().catch((error) => {
    console.error('Error running CI tests:', error);
    process.exit(1);
  });
}

export { runTest, testRemoteServer, CONFIG };
