import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListResourcesRequestSchema,
  ListToolsRequestSchema,
  ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

// Core 12-Factor Infrastructure
import { ToolRegistry, ToolExecutor } from './core/tool-framework.js';
import { SchemaValidator } from './core/validation.js';
import { getSQLiteManager, ensureDatabaseReady } from './storage/sqlite-manager.js';
import { RequestContext, ToolRegistration } from './core/types.js';
import { convertToolResultToMCP } from './core/mcp-adapter.js';

// New 12-Factor Modules  
import { setupAgileManagementTools } from './modules/agile-management/tools.js';
import { setupKanbanTools } from './modules/kanban/tools.js';
import { setupMemoryManagementTools } from './modules/memory-management/tools.js';
import { setupADRTools } from './modules/adr-management/tools.js';
import { setupDevelopmentTools } from './modules/development/tools.js';
import { setupWorkspaceTools } from './modules/workspace/tools.js';
// import { setupHumanInteractionTools } from './modules/human-interaction/index.js'; // TODO: Migrate this module
import { setupLocalAITools as setupLocalAIToolsNew } from './modules/local-ai/tools.js';
import { setupDocumentationTools as setupDocumentationToolsNew } from './modules/documentation/tools.js';
import { setupProductRequirementsTools } from './modules/product-requirements/tools.js';
import { setupRAGRetrievalTools } from './modules/rag-retrieval/tools.js';
import { setupProcessAutomationTools } from './modules/process-automation/tools.js';

// Legacy modules (to be migrated)
// import { setupProjectInitTools } from './modules/project-init/index.js';
// import { setupCodeIntelligenceTools } from './modules/code-intelligence/index.js'; // Module not fully implemented
// import { setupLocalAITools } from './modules/local-ai/index.js'; // Duplicate - using tools.js import
import { setupDeveloperWorkflowTools } from './modules/developer-workflow/tools.js';
// import { setupDocumentationTools } from './modules/documentation/index.js'; // Duplicate - using tools.js import
import { setupDataManagementTools } from './modules/data-management/tools.js';

// Dashboard and monitoring
import { DashboardServer } from './web-dashboard/server.js';
import { PerformanceMonitor } from './utils/performance-monitor.js';
import { SecurityManager } from './utils/security-manager.js';
import { ErrorHandler } from './utils/error-handler.js';

// Get version from package.json
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
const VERSION = packageJson.version;

/**
 * Atlas MCP Server - 12-Factor Implementation
 * 
 * Implements all 12 factors for enterprise-grade MCP servers:
 * 1. Separation of Concerns - Modular architecture
 * 2. Deterministic Execution - Structured outputs
 * 3. Stateless Processes - RequestContext pattern
 * 4. Structured Outputs - JSON Schema validation
 * 5. Contextual Memory - SQLite persistence
 * 6. Configuration as Code - Environment variables
 * 7. Contact Humans - Approval workflows
 * 8. Capabilities-based Authorization - Security layer
 * 9. Error Self-Healing - Structured error handling
 * 10. Performance Observability - Monitoring and metrics
 * 11. Request Context - Tracing and logging
 * 12. Production Infrastructure - Deployment ready
 */
export class AtlasServer {
  private server: Server;
  private toolRegistry: ToolRegistry;
  private toolExecutor: ToolExecutor;
  private schemaValidator: SchemaValidator;
  private sqliteManager: typeof getSQLiteManager;
  private dashboardServer?: DashboardServer;
  private performanceMonitor: PerformanceMonitor;
  private securityManager: SecurityManager;
  private errorHandler: ErrorHandler;

  constructor() {
    this.performanceMonitor = PerformanceMonitor.getInstance();
    this.securityManager = SecurityManager.getInstance();
    this.errorHandler = new ErrorHandler();
    this.sqliteManager = getSQLiteManager;
    
    // Initialize 12-factor infrastructure
    this.schemaValidator = new SchemaValidator();
    this.toolRegistry = new ToolRegistry();
    this.toolExecutor = new ToolExecutor(this.toolRegistry);
    
    this.server = new Server(
      {
        name: 'atlas',
        version: VERSION,
      },
      {
        capabilities: {
          resources: {},
          tools: {},
          prompts: {},
        },
      }
    );

    this.setupHandlers();
  }

  /**
   * Initialize the server and register all modules
   */
  async initialize(): Promise<void> {
    try {
      console.error('🚀 Initializing Atlas MCP Server (12-Factor Architecture)...');
      
      // Initialize SQLite database with automatic migration
      await this.sqliteManager().initialize();
      console.error('✅ Database initialized with migration support');

      // Register 12-factor compliant modules
      await this.registerNewModules();

      // Register legacy modules (to be migrated)
      await this.registerLegacyModules();

      // Initialize dashboard
      await this.initializeDashboard();

      // Start performance monitoring
      this.performanceMonitor.startMonitoring();

      console.error(`✅ Atlas Server v${VERSION} initialized successfully`);
      console.error(`📊 Registered ${this.toolRegistry.getToolCount()} tools across ${this.toolRegistry.getModuleCount()} modules`);
      
    } catch (error) {
      console.error('❌ Failed to initialize Atlas Server:', error);
      throw error;
    }
  }

  /**
   * Register new 12-factor compliant modules
   */
  private async registerNewModules(): Promise<void> {
    const modules = [
      // { name: 'human-interaction', setup: setupHumanInteractionTools }, // TODO: Migrate this module
      { name: 'agile-management', setup: setupAgileManagementTools },
      { name: 'kanban', setup: setupKanbanTools },
      { name: 'memory-management', setup: setupMemoryManagementTools },
      { name: 'adr-management', setup: setupADRTools },
      { name: 'development', setup: setupDevelopmentTools },
      { name: 'workspace', setup: setupWorkspaceTools },
      { name: 'local-ai', setup: setupLocalAIToolsNew },
      { name: 'documentation', setup: setupDocumentationToolsNew },
      { name: 'product-requirements', setup: setupProductRequirementsTools },
      { name: 'rag-retrieval', setup: setupRAGRetrievalTools },
      { name: 'process-automation', setup: setupProcessAutomationTools },
      { name: 'developer-workflow', setup: setupDeveloperWorkflowTools },
      { name: 'data-management', setup: setupDataManagementTools },
    ];

    for (const module of modules) {
      try {
        const registration = await module.setup();
        this.toolRegistry.registerModule(registration);
        console.error(`✅ Registered ${module.name} module (${registration.tools.length} tools)`);
      } catch (error) {
        console.error(`❌ Failed to register ${module.name} module:`, error);
        throw error;
      }
    }
  }

  /**
   * Register legacy modules (to be gradually migrated)
   */
  private async registerLegacyModules(): Promise<void> {
    const legacyModules = [
      // { name: 'project-init', setup: () => setupProjectInitTools(this.server) },
      // { name: 'code-intelligence', setup: () => setupCodeIntelligenceTools(this.server) }, // Module not implemented
      // All modules have been migrated to 12-factor pattern
    ];

    for (const module of legacyModules) {
      try {
        await module.setup();
        console.error(`✅ Registered legacy ${module.name} module`);
      } catch (error) {
        console.warn(`⚠️ Failed to register legacy ${module.name} module:`, error);
        // Continue with other modules
      }
    }
  }

  /**
   * Find an available port starting from the given port number
   */
  private async findAvailablePort(startPort: number): Promise<number> {
    const net = await import('net');
    
    return new Promise((resolve, reject) => {
      const server = net.createServer();
      
      server.listen(startPort, () => {
        const port = (server.address() as any)?.port;
        server.close(() => {
          if (port) {
            resolve(port);
          } else {
            reject(new Error('Failed to get port'));
          }
        });
      });
      
      server.on('error', (err: any) => {
        if (err.code === 'EADDRINUSE') {
          // Try next port
          this.findAvailablePort(startPort + 1).then(resolve).catch(reject);
        } else {
          reject(err);
        }
      });
    });
  }

  /**
   * Initialize the web dashboard
   */
  private async initializeDashboard(): Promise<void> {
    try {
      const dashboardEnabled = process.env.ATLAS_DASHBOARD_ENABLED !== 'false';
      
      // Use dynamic port selection if not specified
      let dashboardPort: number;
      if (process.env.ATLAS_DASHBOARD_PORT) {
        dashboardPort = parseInt(process.env.ATLAS_DASHBOARD_PORT);
      } else {
        // Use different port ranges for dev vs production
        const isDevelopment = process.env.NODE_ENV === 'development' || 
                             process.argv[0].includes('tsx');
        const startPort = isDevelopment ? 3100 : 3001; // Dev uses 3100+, production uses 3001+
        dashboardPort = await this.findAvailablePort(startPort);
      }

      if (dashboardEnabled) {
        // Create an agileManager with methods needed by the dashboard
        const agileManager = {
          // Velocity calculation for dashboard charts
          getVelocityData: async (options: { teamName?: string; sprints?: number }) => {
            try {
              const db = await ensureDatabaseReady();
              const limit = options?.sprints || 5;
              
              // Get completed sprints
              const sprintsResult = await db.query(
                `SELECT * FROM agile_sprints 
                 WHERE status = 'completed' 
                 ORDER BY end_date DESC 
                 LIMIT ?`,
                [limit]
              );
              
              if (!sprintsResult.success || !sprintsResult.data) {
                return null;
              }
              
              const velocityData = {
                labels: [],
                datasets: [{
                  label: 'Velocity',
                  data: [],
                  backgroundColor: 'rgba(59, 130, 246, 0.5)',
                  borderColor: 'rgb(59, 130, 246)',
                  borderWidth: 2
                }]
              };
              
              for (const sprint of sprintsResult.data.reverse()) {
                velocityData.labels.push(sprint.name);
                velocityData.datasets[0].data.push(sprint.story_points_completed || 0);
              }
              
              return velocityData;
            } catch (error) {
              console.error('Error calculating velocity:', error);
              return null;
            }
          },
          
          // Burndown data for dashboard charts
          getBurndownData: async (sprintId: string) => {
            try {
              const db = await ensureDatabaseReady();
              
              // Get sprint details
              const sprintResult = await db.get(
                'SELECT * FROM agile_sprints WHERE id = ?',
                [sprintId]
              );
              
              if (!sprintResult.success || !sprintResult.data) {
                return null;
              }
              
              const sprint = sprintResult.data;
              const totalPoints = sprint.story_points_planned || 0;
              const completedPoints = sprint.story_points_completed || 0;
              
              // Create simple burndown data
              // In a real implementation, this would track daily progress
              const burndownData = {
                labels: ['Start', 'Current', 'End'],
                datasets: [{
                  label: 'Ideal',
                  data: [totalPoints, totalPoints / 2, 0],
                  borderColor: 'rgba(156, 163, 175, 1)',
                  borderDash: [5, 5],
                  fill: false
                }, {
                  label: 'Actual',
                  data: [totalPoints, totalPoints - completedPoints, null],
                  borderColor: 'rgb(59, 130, 246)',
                  backgroundColor: 'rgba(59, 130, 246, 0.1)',
                  fill: true
                }]
              };
              
              return burndownData;
            } catch (error) {
              console.error('Error getting burndown data:', error);
              return null;
            }
          }
        };
        
        this.dashboardServer = new DashboardServer(
          { 
            enabled: true, 
            port: dashboardPort,
            host: 'localhost',
            features: {
              performance: true,
              security: true,
              agile: true,
              errors: true
            },
            realTimeUpdates: true,
            exportEnabled: true
          },
          this.performanceMonitor,
          this.securityManager,
          this.errorHandler,
          agileManager
        );
        await this.dashboardServer.start();
        console.error(`✅ Dashboard server started on port ${dashboardPort}`);
      } else {
        console.error('📊 Dashboard disabled via configuration');
      }
    } catch (error) {
      console.warn('⚠️ Dashboard initialization failed:', error);
      // Dashboard failure shouldn't break the main server
    }
  }

  /**
   * Setup MCP request handlers
   */
  private setupHandlers(): void {
    // List available tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: this.toolRegistry.getToolManifest()
      };
    });

    // Execute tools using the new framework
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const startTime = Date.now();
      const { name, arguments: args } = request.params;

      try {
        // Create request context
        const context: RequestContext = {
          toolName: name,
          requestId: this.generateRequestId(),
          timestamp: Date.now(),
          userId: this.extractUserId(request),
          projectId: this.extractProjectId(request),
          db: this.sqliteManager(),
          metadata: {}
        };

        // Log request
        console.error(`🔧 Executing tool: ${name} (${context.requestId})`);

        // Execute tool using the framework
        const result = await this.toolExecutor.execute(name, args || {}, context);

        // Log performance
        const duration = Date.now() - startTime;
        this.performanceMonitor.recordToolExecution(name, duration);

        // Convert the result to MCP format using the adapter
        const mcpResponse = convertToolResultToMCP(result, name);
        
        if (result.success) {
          console.error(`✅ Tool ${name} completed in ${duration}ms`);
        } else {
          console.error(`❌ Tool ${name} failed:`, result.error);
        }
        
        return mcpResponse;

      } catch (error) {
        const duration = Date.now() - startTime;
        console.error(`💥 Tool ${name} crashed after ${duration}ms:`, error);
        
        return {
          content: [
            {
              type: 'text',
              text: `Internal error executing ${name}: ${error instanceof Error ? error.message : 'Unknown error'}`,
            }
          ],
          isError: true
        };
      }
    });

    // List resources
    this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
      resources: [
        {
          uri: 'atlas://dashboard',
          name: 'Atlas Dashboard',
          description: 'Web-based project management dashboard',
          mimeType: 'text/html',
        },
        {
          uri: 'atlas://database/stats',
          name: 'Database Statistics',
          description: 'SQLite database statistics and health',
          mimeType: 'application/json',
        },
        {
          uri: 'atlas://tools/manifest',
          name: 'Tools Manifest',
          description: 'List of all available tools and their schemas',
          mimeType: 'application/json',
        }
      ]
    }));

    // Read resources
    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const { uri } = request.params;

      switch (uri) {
        case 'atlas://dashboard':
          return {
            contents: [
              {
                uri,
                mimeType: 'text/html',
                text: `Dashboard available at: ${process.env.ATLAS_DASHBOARD_URL || 'http://localhost:3001'}`
              }
            ]
          };

        case 'atlas://database/stats':
          const stats = await this.sqliteManager().getStats();
          return {
            contents: [
              {
                uri,
                mimeType: 'application/json',
                text: JSON.stringify(stats.data, null, 2)
              }
            ]
          };

        case 'atlas://tools/manifest':
          return {
            contents: [
              {
                uri,
                mimeType: 'application/json',
                text: JSON.stringify(this.toolRegistry.getToolManifest(), null, 2)
              }
            ]
          };

        default:
          throw new Error(`Unknown resource: ${uri}`);
      }
    });
  }

  /**
   * Start the server
   */
  async start(): Promise<void> {
    await this.initialize();
    
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    
    console.error('🌟 Atlas MCP Server is running with 12-factor architecture');
  }

  /**
   * Stop the server
   */
  async stop(): Promise<void> {
    try {
      await this.sqliteManager().close();
      if (this.dashboardServer) {
        await this.dashboardServer.stop();
      }
      this.performanceMonitor.stopMonitoring();
      console.error('👋 Atlas Server stopped gracefully');
    } catch (error) {
      console.error('❌ Error stopping server:', error);
    }
  }

  /**
   * Utility methods
   */
  private generateRequestId(): string {
    return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private extractUserId(request: any): string | undefined {
    // Extract user ID from request metadata if available
    return request.meta?.userId || process.env.ATLAS_DEFAULT_USER_ID || undefined;
  }

  private extractProjectId(request: any): string | undefined {
    // Extract project ID from request metadata if available  
    return request.meta?.projectId || process.env.ATLAS_PROJECT_ID || 'default';
  }
}

// Start the server if this file is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
  const server = new AtlasServer();
  
  // Graceful shutdown
  process.on('SIGINT', async () => {
    console.error('\n📡 Received SIGINT, shutting down gracefully...');
    await server.stop();
    process.exit(0);
  });

  process.on('SIGTERM', async () => {
    console.error('\n📡 Received SIGTERM, shutting down gracefully...');
    await server.stop();
    process.exit(0);
  });

  // Start the server
  server.start().catch(error => {
    console.error('💥 Failed to start Atlas Server:', error);
    process.exit(1);
  });
}