import 'reflect-metadata';
import { configureDIContainer, resetDIContainer, getDIContainer, resolveSessionDataPath, resolveTemplatePath } from './di-config';
import { DI_TOKENS } from './di-tokens';
import { PlaywrightAutomationAdapter, FixtureServer } from '@core/automation/infrastructure//automation';
import { StartAutomationSessionUseCase } from 'apps/companion/main/automation/application/use-cases/StartAutomationSessionUseCase';
import { CheckAuthenticationUseCase } from 'apps/companion/main/automation/application/use-cases/CheckAuthenticationUseCase';
import { InitiateLoginUseCase } from 'apps/companion/main/automation/application/use-cases/InitiateLoginUseCase';
import { ClearSessionUseCase } from 'apps/companion/main/automation/application/use-cases/ClearSessionUseCase';
import { ConfirmCheckoutUseCase } from 'apps/companion/main/automation/application/use-cases/ConfirmCheckoutUseCase';
import { getAutomationMode, AutomationMode, BrowserModeConfigLoader } from '@core/automation/infrastructure/config';
import type { SessionRepositoryPort } from 'apps/companion/main/automation/application/ports/SessionRepositoryPort';
import type { IBrowserAutomation } from 'apps/companion/main/automation/application/ports/ScreenAutomationPort';
import type { AutomationEnginePort } from 'apps/companion/main/automation/application/ports/AutomationEnginePort';
import type { AuthenticationServicePort } from 'apps/companion/main/automation/application/ports/AuthenticationServicePort';
import type { CheckoutConfirmationPort } from 'apps/companion/main/automation/application/ports/CheckoutConfirmationPort';
import type { CheckoutServicePort } from 'apps/companion/main/automation/application/ports/CheckoutServicePort';
import type { LoggerPort } from 'apps/companion/main/automation/application/ports/LoggerPort';
import type { OverlaySyncPort } from 'apps/companion/main/automation/application/ports/OverlaySyncPort';

// Re-export for backward compatibility
export { resolveSessionDataPath, resolveTemplatePath };

export interface BrowserConnectionResult {
  success: boolean;
  error?: string;
}

/**
 * Check if running in fixture hosted mode
 */
function isFixtureHostedMode(): boolean {
  return process.env.NODE_ENV === 'test' && process.env.COMPANION_FIXTURE_HOSTED === '1';
}

/**
 * DIContainer - Facade over TSyringe container for backward compatibility
 */
export class DIContainer {
  private static instance: DIContainer;
  private initialized = false;
  private automationMode: AutomationMode;
  private fixtureServer: FixtureServer | null = null;
  private confirmCheckoutUseCase: ConfirmCheckoutUseCase | null = null;

  private constructor() {
    this.automationMode = getAutomationMode();
  }

  /**
   * Lazily initialize the TSyringe container
   */
  private ensureInitialized(): void {
    if (this.initialized) return;
    
    configureDIContainer();
    
    const logger = getDIContainer().resolve<LoggerPort>(DI_TOKENS.Logger);
    logger.info('DIContainer initialized', {
      automationMode: this.automationMode,
      nodeEnv: process.env.NODE_ENV,
    });
    
    const fixtureMode = isFixtureHostedMode();
    if (fixtureMode) {
      try {
        this.fixtureServer = getDIContainer().resolve<FixtureServer>(DI_TOKENS.FixtureServer);
      } catch {
        // FixtureServer not registered in non-fixture mode
      }
    }
    
    this.initialized = true;
  }

  public static getInstance(): DIContainer {
    if (!DIContainer.instance) {
      DIContainer.instance = new DIContainer();
    }
    return DIContainer.instance;
  }

  public getStartAutomationUseCase(): StartAutomationSessionUseCase {
    this.ensureInitialized();
    return getDIContainer().resolve<StartAutomationSessionUseCase>(DI_TOKENS.StartAutomationUseCase);
  }

  public getSessionRepository(): SessionRepositoryPort {
    this.ensureInitialized();
    return getDIContainer().resolve<SessionRepositoryPort>(DI_TOKENS.SessionRepository);
  }

  public getAutomationEngine(): AutomationEnginePort {
    this.ensureInitialized();
    return getDIContainer().resolve<AutomationEnginePort>(DI_TOKENS.AutomationEngine);
  }

  public getAutomationMode(): AutomationMode {
    return this.automationMode;
  }

  public getBrowserAutomation(): IBrowserAutomation {
    this.ensureInitialized();
    return getDIContainer().resolve<IBrowserAutomation>(DI_TOKENS.BrowserAutomation);
  }

  public getLogger(): LoggerPort {
    this.ensureInitialized();
    return getDIContainer().resolve<LoggerPort>(DI_TOKENS.Logger);
  }

  public getCheckAuthenticationUseCase(): CheckAuthenticationUseCase | null {
    this.ensureInitialized();
    try {
      return getDIContainer().resolve<CheckAuthenticationUseCase>(DI_TOKENS.CheckAuthenticationUseCase);
    } catch {
      return null;
    }
  }

  public getInitiateLoginUseCase(): InitiateLoginUseCase | null {
    this.ensureInitialized();
    try {
      return getDIContainer().resolve<InitiateLoginUseCase>(DI_TOKENS.InitiateLoginUseCase);
    } catch {
      return null;
    }
  }

  public getClearSessionUseCase(): ClearSessionUseCase | null {
    this.ensureInitialized();
    try {
      return getDIContainer().resolve<ClearSessionUseCase>(DI_TOKENS.ClearSessionUseCase);
    } catch {
      return null;
    }
  }

  public getAuthenticationService(): AuthenticationServicePort | null {
    this.ensureInitialized();
    try {
      return getDIContainer().resolve<AuthenticationServicePort>(DI_TOKENS.AuthenticationService);
    } catch {
      return null;
    }
  }

  public setConfirmCheckoutUseCase(checkoutConfirmationPort: CheckoutConfirmationPort): void {
    this.ensureInitialized();
    const checkoutService = getDIContainer().resolve<CheckoutServicePort>(DI_TOKENS.CheckoutService);
    this.confirmCheckoutUseCase = new ConfirmCheckoutUseCase(
      checkoutService,
      checkoutConfirmationPort
    );
  }

  public getConfirmCheckoutUseCase(): ConfirmCheckoutUseCase | null {
    return this.confirmCheckoutUseCase;
  }

  public getBrowserModeConfigLoader(): BrowserModeConfigLoader {
    this.ensureInitialized();
    return getDIContainer().resolve<BrowserModeConfigLoader>(DI_TOKENS.BrowserModeConfigLoader);
  }

  public getOverlaySyncPort(): OverlaySyncPort {
    this.ensureInitialized();
    return getDIContainer().resolve<OverlaySyncPort>(DI_TOKENS.OverlaySyncPort);
  }

  public async initializeBrowserConnection(): Promise<BrowserConnectionResult> {
    this.ensureInitialized();
    const fixtureMode = isFixtureHostedMode();
    const logger = this.getLogger();
    
    logger.info('Initializing automation connection', { mode: this.automationMode, fixtureMode });

    if (this.automationMode === 'production' || this.automationMode === 'development' || fixtureMode) {
      try {
        if (fixtureMode && this.fixtureServer && !this.fixtureServer.isRunning()) {
          await this.fixtureServer.start();
        }
        const browserAutomation = this.getBrowserAutomation();
        const playwrightAdapter = browserAutomation as PlaywrightAutomationAdapter;
        const result = await playwrightAdapter.connect();
        
        if (!result.success) {
          logger.error(
            'Automation connection failed',
            new Error(result.error || 'Unknown error'),
            { mode: this.automationMode }
          );
          return { success: false, error: result.error ?? 'Unknown error' };
        }

        const isConnected = playwrightAdapter.isConnected();
        const page = playwrightAdapter.getPage();

        if (!isConnected || !page) {
          const errorMsg = 'Browser not connected';
          logger.error(
            'Automation connection reported success but has no usable page',
            new Error(errorMsg),
            { mode: this.automationMode, isConnected, hasPage: !!page }
          );
          return { success: false, error: errorMsg };
        }

        logger.info('Automation connection established', {
          mode: this.automationMode,
          adapter: 'Playwright',
        });
        return { success: true };
      } catch (error) {
        const errorMsg = error instanceof Error ? error.message : 'Failed to initialize Playwright';
        logger.error(
          'Automation connection failed',
          error instanceof Error ? error : new Error(errorMsg),
          { mode: this.automationMode }
        );
        return {
          success: false,
          error: errorMsg,
        };
      }
    }

    logger.debug('Test mode - no automation connection needed');
    return { success: true };
  }

  public async shutdown(): Promise<void> {
    this.ensureInitialized();
    const logger = this.getLogger();
    logger.info('DIContainer shutting down');

    const browserAutomation = this.getBrowserAutomation();
    if (browserAutomation && 'disconnect' in browserAutomation) {
      try {
        await (browserAutomation as PlaywrightAutomationAdapter).disconnect();
        logger.info('Automation adapter disconnected');
      } catch (error) {
        logger.error(
          'Error disconnecting automation adapter',
          error instanceof Error ? error : new Error('Unknown error')
        );
      }
    }

    if (this.fixtureServer && this.fixtureServer.isRunning()) {
      try {
        await this.fixtureServer.stop();
        logger.info('FixtureServer stopped');
      } catch (error) {
        logger.error('Error stopping FixtureServer', error instanceof Error ? error : new Error('Unknown error'));
      } finally {
        this.fixtureServer = null;
      }
    }

    logger.info('DIContainer shutdown complete');
  }

  public refreshBrowserAutomation(): void {
    this.ensureInitialized();
    // Reconfigure the entire container to pick up new browser mode settings
    resetDIContainer();
    this.initialized = false;
    this.ensureInitialized();
    
    const logger = this.getLogger();
    const browserModeConfigLoader = this.getBrowserModeConfigLoader();
    logger.info('Browser automation refreshed from updated BrowserModeConfigLoader', {
      browserMode: browserModeConfigLoader.load().mode,
    });
  }

  public static resetInstance(): void {
    resetDIContainer();
    DIContainer.instance = undefined as unknown as DIContainer;
  }
}