/**
 * ServiceApplication - Common service initialization and lifecycle management
 * Encapsulates common patterns for Hono-based microservices
 */

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { getLogger, setLoggerConfig, shutdownLoggers, type Logger } from '@stock-bot/logger';
import { Shutdown } from '@stock-bot/shutdown';
import type { AppConfig as StockBotAppConfig, UnifiedAppConfig } from '@stock-bot/config';
import { toUnifiedConfig } from '@stock-bot/config';
import type { IServiceContainer } from '@stock-bot/types';
import type { ServiceContainer } from './awilix-container';

/**
 * Configuration for ServiceApplication
 */
export interface ServiceApplicationConfig {
  /** Service name for logging and identification */
  serviceName: string;
  
  /** CORS configuration - if not provided, uses permissive defaults */
  corsConfig?: Parameters<typeof cors>[0];
  
  /** Whether to enable handler initialization */
  enableHandlers?: boolean;
  
  /** Whether to enable scheduled job creation */
  enableScheduledJobs?: boolean;
  
  /** Custom shutdown timeout in milliseconds */
  shutdownTimeout?: number;
  
  /** Service metadata for info endpoint */
  serviceMetadata?: {
    version?: string;
    description?: string;
    endpoints?: Record<string, string>;
  };
  
  /** Whether to add a basic info endpoint at root */
  addInfoEndpoint?: boolean;
}

/**
 * Lifecycle hooks for service customization
 */
export interface ServiceLifecycleHooks {
  /** Called after container is created but before routes */
  onContainerReady?: (container: IServiceContainer) => Promise<void> | void;
  
  /** Called after app is created but before routes are mounted */
  onAppReady?: (app: Hono, container: IServiceContainer) => Promise<void> | void;
  
  /** Called after routes are mounted but before server starts */
  onBeforeStart?: (app: Hono, container: IServiceContainer) => Promise<void> | void;
  
  /** Called after successful server startup */
  onStarted?: (port: number) => Promise<void> | void;
  
  /** Called during shutdown before cleanup */
  onBeforeShutdown?: () => Promise<void> | void;
}

/**
 * ServiceApplication - Manages the complete lifecycle of a microservice
 */
export class ServiceApplication {
  private config: UnifiedAppConfig;
  private serviceConfig: ServiceApplicationConfig;
  private hooks: ServiceLifecycleHooks;
  private logger: Logger;
  
  private container: ServiceContainer | null = null;
  private serviceContainer: IServiceContainer | null = null;
  private app: Hono | null = null;
  private server: ReturnType<typeof Bun.serve> | null = null;
  private shutdown: Shutdown;
  
  constructor(
    config: StockBotAppConfig | UnifiedAppConfig,
    serviceConfig: ServiceApplicationConfig,
    hooks: ServiceLifecycleHooks = {}
  ) {
    // Convert to unified config
    this.config = toUnifiedConfig(config);
    
    // Ensure service name is set in config
    if (!this.config.service.serviceName) {
      this.config.service.serviceName = serviceConfig.serviceName;
    }
    
    this.serviceConfig = {
      shutdownTimeout: 15000,
      enableHandlers: false,
      enableScheduledJobs: false,
      addInfoEndpoint: true,
      ...serviceConfig,
    };
    this.hooks = hooks;
    
    // Initialize logger configuration
    this.configureLogger();
    this.logger = getLogger(this.serviceConfig.serviceName);
    
    // Initialize shutdown manager
    this.shutdown = Shutdown.getInstance({ 
      timeout: this.serviceConfig.shutdownTimeout 
    });
  }
  
  /**
   * Configure logger based on application config
   */
  private configureLogger(): void {
    if (this.config.log) {
      setLoggerConfig({
        logLevel: this.config.log.level,
        logConsole: true,
        logFile: false,
        environment: this.config.environment,
        hideObject: this.config.log.hideObject,
      });
    }
  }
  
  /**
   * Create and configure Hono application with CORS
   */
  private createApp(): Hono {
    const app = new Hono();
    
    // Add CORS middleware with service-specific or default configuration
    const corsConfig = this.serviceConfig.corsConfig || {
      origin: '*',
      allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
      allowHeaders: ['Content-Type', 'Authorization'],
      credentials: false,
    };
    
    app.use('*', cors(corsConfig));
    
    // Add basic info endpoint if enabled
    if (this.serviceConfig.addInfoEndpoint) {
      const metadata = this.serviceConfig.serviceMetadata || {};
      app.get('/', c => {
        return c.json({
          name: this.serviceConfig.serviceName,
          version: metadata.version || '1.0.0',
          description: metadata.description,
          status: 'running',
          timestamp: new Date().toISOString(),
          endpoints: metadata.endpoints || {},
        });
      });
    }
    
    return app;
  }
  
  /**
   * Register graceful shutdown handlers
   */
  private registerShutdownHandlers(): void {
    // Priority 1: Queue system (highest priority)
    if (this.serviceConfig.enableScheduledJobs) {
      this.shutdown.onShutdownHigh(async () => {
        this.logger.info('Shutting down queue system...');
        try {
          const queueManager = this.container?.resolve('queueManager');
          if (queueManager) {
            await queueManager.shutdown();
          }
          this.logger.info('Queue system shut down');
        } catch (error) {
          this.logger.error('Error shutting down queue system', { error });
        }
      }, 'Queue System');
    }
    
    // Priority 1: HTTP Server (high priority)
    this.shutdown.onShutdownHigh(async () => {
      if (this.server) {
        this.logger.info('Stopping HTTP server...');
        try {
          this.server.stop();
          this.logger.info('HTTP server stopped');
        } catch (error) {
          this.logger.error('Error stopping HTTP server', { error });
        }
      }
    }, 'HTTP Server');
    
    // Custom shutdown hook
    if (this.hooks.onBeforeShutdown) {
      this.shutdown.onShutdownHigh(async () => {
        try {
          await this.hooks.onBeforeShutdown!();
        } catch (error) {
          this.logger.error('Error in custom shutdown hook', { error });
        }
      }, 'Custom Shutdown');
    }
    
    // Priority 2: Services and connections (medium priority)
    this.shutdown.onShutdownMedium(async () => {
      this.logger.info('Disposing services and connections...');
      try {
        if (this.container) {
          // Disconnect database clients
          const mongoClient = this.container.resolve('mongoClient');
          if (mongoClient?.disconnect) {
            await mongoClient.disconnect();
          }
          
          const postgresClient = this.container.resolve('postgresClient');
          if (postgresClient?.disconnect) {
            await postgresClient.disconnect();
          }
          
          const questdbClient = this.container.resolve('questdbClient');
          if (questdbClient?.disconnect) {
            await questdbClient.disconnect();
          }
          
          this.logger.info('All services disposed successfully');
        }
      } catch (error) {
        this.logger.error('Error disposing services', { error });
      }
    }, 'Services');
    
    // Priority 3: Logger shutdown (lowest priority - runs last)
    this.shutdown.onShutdownLow(async () => {
      try {
        this.logger.info('Shutting down loggers...');
        await shutdownLoggers();
        // Don't log after shutdown
      } catch {
        // Silently ignore logger shutdown errors
      }
    }, 'Loggers');
  }
  
  /**
   * Start the service with full initialization
   */
  async start(
    containerFactory: (config: UnifiedAppConfig) => Promise<ServiceContainer>,
    routeFactory: (container: IServiceContainer) => Hono,
    handlerInitializer?: (container: IServiceContainer) => Promise<void>
  ): Promise<void> {
    this.logger.info(`Initializing ${this.serviceConfig.serviceName} service...`);
    
    try {
      // Create and initialize container
      this.logger.debug('Creating DI container...');
      // Config already has service name from constructor
      this.container = await containerFactory(this.config);
      this.serviceContainer = this.container.resolve('serviceContainer');
      this.logger.info('DI container created and initialized');
      
      // Call container ready hook
      if (this.hooks.onContainerReady) {
        await this.hooks.onContainerReady(this.serviceContainer);
      }
      
      // Create Hono application
      this.app = this.createApp();
      
      // Call app ready hook
      if (this.hooks.onAppReady) {
        await this.hooks.onAppReady(this.app, this.serviceContainer);
      }
      
      // Initialize handlers if enabled
      if (this.serviceConfig.enableHandlers && handlerInitializer) {
        this.logger.debug('Initializing handlers...');
        await handlerInitializer(this.serviceContainer);
        this.logger.info('Handlers initialized');
      }
      
      // Create and mount routes
      const routes = routeFactory(this.serviceContainer);
      this.app.route('/', routes);
      
      // Initialize scheduled jobs if enabled
      if (this.serviceConfig.enableScheduledJobs) {
        await this.initializeScheduledJobs();
      }
      
      // Call before start hook
      if (this.hooks.onBeforeStart) {
        await this.hooks.onBeforeStart(this.app, this.serviceContainer);
      }
      
      // Register shutdown handlers
      this.registerShutdownHandlers();
      
      // Start HTTP server
      const port = this.config.service.port;
      this.server = Bun.serve({
        port,
        fetch: this.app.fetch,
        development: this.config.environment === 'development',
      });
      
      this.logger.info(`${this.serviceConfig.serviceName} service started on port ${port}`);
      
      // Call started hook
      if (this.hooks.onStarted) {
        await this.hooks.onStarted(port);
      }
      
    } catch (error) {
      this.logger.error('DETAILED ERROR:', error);
      this.logger.error('Failed to start service', {
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
        details: JSON.stringify(error, null, 2),
      });
      throw error;
    }
  }
  
  /**
   * Initialize scheduled jobs from handler registry
   */
  private async initializeScheduledJobs(): Promise<void> {
    if (!this.container) {
      throw new Error('Container not initialized');
    }
    
    this.logger.debug('Creating scheduled jobs from registered handlers...');
    const { handlerRegistry } = await import('@stock-bot/handlers');
    const allHandlers = handlerRegistry.getAllHandlersWithSchedule();
    
    let totalScheduledJobs = 0;
    for (const [handlerName, config] of allHandlers) {
      if (config.scheduledJobs && config.scheduledJobs.length > 0) {
        const queueManager = this.container.resolve('queueManager');
        if (!queueManager) {
          this.logger.error('Queue manager is not initialized, cannot create scheduled jobs');
          continue;
        }
        const queue = queueManager.getQueue(handlerName);
        
        for (const scheduledJob of config.scheduledJobs) {
          // Include handler and operation info in job data
          const jobData = {
            handler: handlerName,
            operation: scheduledJob.operation,
            payload: scheduledJob.payload,
          };
          
          // Build job options from scheduled job config
          const jobOptions = {
            priority: scheduledJob.priority,
            delay: scheduledJob.delay,
            repeat: {
              immediately: scheduledJob.immediately,
            },
          };
          
          await queue.addScheduledJob(
            scheduledJob.operation,
            jobData,
            scheduledJob.cronPattern,
            jobOptions
          );
          totalScheduledJobs++;
          this.logger.debug('Scheduled job created', {
            handler: handlerName,
            operation: scheduledJob.operation,
            cronPattern: scheduledJob.cronPattern,
            immediately: scheduledJob.immediately,
            priority: scheduledJob.priority,
          });
        }
      }
    }
    this.logger.info('Scheduled jobs created', { totalJobs: totalScheduledJobs });
    
    // Start queue workers
    this.logger.debug('Starting queue workers...');
    const queueManager = this.container.resolve('queueManager');
    if (queueManager) {
      queueManager.startAllWorkers();
      this.logger.info('Queue workers started');
    }
  }
  
  /**
   * Stop the service gracefully
   */
  async stop(): Promise<void> {
    this.logger.info(`Stopping ${this.serviceConfig.serviceName} service...`);
    await this.shutdown.shutdown();
  }
  
  /**
   * Get the service container (for testing or advanced use cases)
   */
  getServiceContainer(): IServiceContainer | null {
    return this.serviceContainer;
  }
  
  /**
   * Get the Hono app (for testing or advanced use cases)
   */
  getApp(): Hono | null {
    return this.app;
  }
}