import { asClass, asFunction, createContainer, InjectionMode, type AwilixContainer } from 'awilix';
import type { BaseAppConfig as StockBotAppConfig, UnifiedAppConfig } from '@stock-bot/config';
import { toUnifiedConfig } from '@stock-bot/config';
import { HandlerRegistry } from '@stock-bot/handler-registry';
import { appConfigSchema, type AppConfig } from '../config/schemas';
import {
  registerApplicationServices,
  registerCacheServices,
  registerCoreServices,
  registerDatabaseServices,
} from '../registrations';
import { HandlerScanner } from '../scanner';
import { ServiceLifecycleManager } from '../utils/lifecycle';
import type { ContainerBuildOptions, ServiceDefinitions } from './types';

export class ServiceContainerBuilder {
  private config: Partial<AppConfig> = {};
  private unifiedConfig: UnifiedAppConfig | null = null;
  private options: ContainerBuildOptions = {
    enableCache: true,
    enableQueue: true,
    enableMongoDB: true,
    enablePostgres: true,
    enableQuestDB: true,
    enableBrowser: true,
    enableProxy: true,
    skipInitialization: false,
    initializationTimeout: 30000,
  };

  withConfig(config: AppConfig | StockBotAppConfig | UnifiedAppConfig): this {
    // Convert to unified config format
    this.unifiedConfig = toUnifiedConfig(config);
    this.config = this.transformStockBotConfig(this.unifiedConfig);
    return this;
  }

  withOptions(options: Partial<ContainerBuildOptions>): this {
    Object.assign(this.options, options);
    return this;
  }

  enableService(
    service: keyof Omit<ContainerBuildOptions, 'skipInitialization' | 'initializationTimeout'>,
    enabled = true
  ): this {
    this.options[service] = enabled;
    return this;
  }

  skipInitialization(skip = true): this {
    this.options.skipInitialization = skip;
    return this;
  }

  async build(): Promise<AwilixContainer<ServiceDefinitions>> {
    // Validate and prepare config
    const validatedConfig = this.prepareConfig();

    // Create container
    const container = createContainer<ServiceDefinitions>({
      injectionMode: InjectionMode.PROXY,
      strict: true,
    });

    // Register services
    this.registerServices(container, validatedConfig);

    // Initialize services if not skipped
    if (!this.options.skipInitialization) {
      const lifecycleManager = new ServiceLifecycleManager();
      await lifecycleManager.initializeServices(container, this.options.initializationTimeout);
    }

    return container;
  }

  private prepareConfig(): AppConfig {
    const finalConfig = this.applyServiceOptions(this.config);
    return appConfigSchema.parse(finalConfig);
  }

  private applyServiceOptions(config: Partial<AppConfig>): AppConfig {
    // Ensure questdb config has the right field names for DI
    const questdbConfig = config.questdb
      ? {
          ...config.questdb,
          influxPort: (config.questdb as any).influxPort || (config.questdb as any).ilpPort || 9009,
        }
      : {
          enabled: true,
          host: 'localhost',
          httpPort: 9000,
          pgPort: 8812,
          influxPort: 9009,
          database: 'questdb',
        };

    return {
      redis: config.redis || {
        enabled: this.options.enableCache ?? true,
        host: 'localhost',
        port: 6379,
        db: 0,
      },
      mongodb: config.mongodb || {
        enabled: this.options.enableMongoDB ?? true,
        uri: '',
        database: '',
      },
      postgres: config.postgres || {
        enabled: this.options.enablePostgres ?? true,
        host: 'localhost',
        port: 5432,
        database: 'postgres',
        user: 'postgres',
        password: 'postgres',
      },
      questdb: this.options.enableQuestDB ? questdbConfig : undefined,
      proxy: this.options.enableProxy
        ? config.proxy || { enabled: false, cachePrefix: 'proxy:', ttl: 3600 }
        : undefined,
      browser: this.options.enableBrowser
        ? config.browser || { headless: true, timeout: 30000 }
        : undefined,
      queue: this.options.enableQueue
        ? config.queue || {
            enabled: true,
            workers: 1,
            concurrency: 1,
            enableScheduledJobs: true,
            defaultJobOptions: {
              attempts: 3,
              backoff: { type: 'exponential' as const, delay: 1000 },
              removeOnComplete: 100,
              removeOnFail: 100,
            },
          }
        : undefined,
      service: config.service,
    };
  }

  private registerServices(
    container: AwilixContainer<ServiceDefinitions>,
    config: AppConfig
  ): void {
    // Register handler infrastructure first
    container.register({
      handlerRegistry: asClass(HandlerRegistry).singleton(),
      handlerScanner: asFunction(({ handlerRegistry }) => {
        return new HandlerScanner(handlerRegistry, container, {
          serviceName: config.service?.serviceName || config.service?.name,
          autoRegister: true,
        });
      }).singleton(),
    });

    registerCoreServices(container, config);
    registerCacheServices(container, config);
    registerDatabaseServices(container, config);
    registerApplicationServices(container, config);

    // Register service container aggregate
    container.register({
      serviceContainer: asFunction(
        ({
          config: _config,
          logger,
          cache,
          globalCache,
          proxyManager,
          browser,
          queueManager,
          mongoClient,
          postgresClient,
          questdbClient,
        }) => ({
          logger,
          cache,
          globalCache,
          proxy: proxyManager, // Map proxyManager to proxy
          browser,
          queueManager, // Provide queueManager directly
          mongodb: mongoClient, // Map mongoClient to mongodb
          postgres: postgresClient, // Map postgresClient to postgres
          questdb: questdbClient, // Map questdbClient to questdb
        })
      ).singleton(),
    });
  }

  private transformStockBotConfig(config: UnifiedAppConfig): Partial<AppConfig> {
    // Unified config already has flat structure, just extract what we need
    // Handle questdb field name mapping
    const questdb = config.questdb
      ? {
          enabled: config.questdb.enabled || true,
          host: config.questdb.host || 'localhost',
          httpPort: config.questdb.httpPort || 9000,
          pgPort: config.questdb.pgPort || 8812,
          influxPort: (config.questdb as any).influxPort || (config.questdb as any).ilpPort || 9009,
          database: config.questdb.database || 'questdb',
        }
      : undefined;

    return {
      redis: config.redis,
      mongodb: config.mongodb,
      postgres: config.postgres,
      questdb,
      queue: config.queue,
      browser: config.browser,
      proxy: config.proxy,
      service: config.service,
    };
  }
}
