import 'reflect-metadata';
import { container, Lifecycle } from 'tsyringe';
import { DI_TOKENS } from './di-tokens';
import * as path from 'path';
import * as os from 'os';

// Domain & Application
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 { LoggerPort } from 'apps/companion/main/automation/application/ports/LoggerPort';
import type { OverlaySyncPort } from 'apps/companion/main/automation/application/ports/OverlaySyncPort';
import type { CheckoutServicePort } from 'apps/companion/main/automation/application/ports/CheckoutServicePort';
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 { OverlaySyncService } from 'apps/companion/main/automation/application/services/OverlaySyncService';
import type { IAutomationLifecycleEmitter } from '@core/automation/infrastructure//IAutomationLifecycleEmitter';

// Infrastructure
import { InMemorySessionRepository } from 'apps/companion/main/automation/infrastructure/repositories/InMemorySessionRepository';
import {
  MockBrowserAutomationAdapter,
  PlaywrightAutomationAdapter,
  AutomationAdapterMode,
  FixtureServer,
} from '@core/automation/infrastructure//automation';
import { MockAutomationEngineAdapter } from '@core/automation/infrastructure//automation/engine/MockAutomationEngineAdapter';
import { AutomationEngineAdapter } from '@core/automation/infrastructure//automation/engine/AutomationEngineAdapter';
import { ConsoleLogAdapter } from '@core/automation/infrastructure//logging/ConsoleLogAdapter';
import { NoOpLogAdapter } from '@core/automation/infrastructure//logging/NoOpLogAdapter';
import {
  loadAutomationConfig,
  getAutomationMode,
  AutomationMode,
  BrowserModeConfigLoader,
} from '@core/automation/infrastructure/config';
import { loadLoggingConfig } from '@core/automation/infrastructure/config/LoggingConfig';

// Electron app safe wrapper
let electronApp: {
  getAppPath?: () => string;
  getPath?: (name: string) => string;
  isPackaged?: boolean;
} | undefined;

try {
  const _electron = require('electron');
  electronApp = _electron?.app;
} catch {
  electronApp = undefined;
}

/**
 * Resolve session data path with test-tolerance
 */
export function resolveSessionDataPath(): string {
  const userDataPath =
    electronApp?.getPath?.('userData') ?? path.join(process.cwd(), 'userData') ?? os.tmpdir();
  return path.join(userDataPath, 'iracing-session');
}

/**
 * Resolve template path with test-tolerance
 */
export function resolveTemplatePath(): string {
  const appPath = electronApp?.getAppPath?.() ?? process.cwd();
  const isPackaged = electronApp?.isPackaged ?? false;

  if (isPackaged) {
    return path.join(appPath, 'resources/templates/iracing');
  }

  return path.join(appPath, '../../resources/templates/iracing');
}

/**
 * Determine adapter mode based on environment
 */
function getAdapterMode(envMode: AutomationMode): AutomationAdapterMode {
  return envMode === 'test' ? 'mock' : 'real';
}

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

/**
 * Configure the DI container with all bindings
 */
export function configureDIContainer(): void {
  // Clear any existing registrations
  container.clearInstances();

  // Configuration values
  const automationMode = getAutomationMode();
  const config = loadAutomationConfig();
  const loggingConfig = loadLoggingConfig();
  const fixtureMode = isFixtureHostedMode();

  container.registerInstance(DI_TOKENS.AutomationMode, automationMode);

  // Logger (singleton)
  const logger = process.env.NODE_ENV === 'test' ? new NoOpLogAdapter() : new ConsoleLogAdapter();
  container.registerInstance<LoggerPort>(DI_TOKENS.Logger, logger);

  // Browser Mode Config Loader (singleton)
  const browserModeConfigLoader = new BrowserModeConfigLoader();
  if (process.env.NODE_ENV === 'development') {
    browserModeConfigLoader.setDevelopmentMode('headed');
  }
  container.registerInstance(DI_TOKENS.BrowserModeConfigLoader, browserModeConfigLoader);

  // Session Repository (singleton)
  container.register<SessionRepositoryPort>(
    DI_TOKENS.SessionRepository,
    { useFactory: (c) => new InMemorySessionRepository(c.resolve(DI_TOKENS.Logger)) },
    { lifecycle: Lifecycle.Singleton }
  );

  // Browser Automation Adapter (singleton)
  const browserModeConfig = browserModeConfigLoader.load();
  const adapterMode = getAdapterMode(config.mode);
  const absoluteTemplatePath = resolveTemplatePath();
  const sessionDataPath = resolveSessionDataPath();
  const safeAppPath = electronApp?.getAppPath?.() ?? process.cwd();
  const safeIsPackaged = electronApp?.isPackaged ?? false;

  logger.debug('Resolved paths', {
    absoluteTemplatePath,
    sessionDataPath,
    appPath: safeAppPath,
    isPackaged: safeIsPackaged,
    cwd: process.cwd(),
  });

  logger.info('Creating browser automation adapter', {
    envMode: config.mode,
    adapterMode,
    browserMode: browserModeConfig.mode,
    browserModeSource: browserModeConfig.source,
  });

  let browserAutomation: PlaywrightAutomationAdapter | MockBrowserAutomationAdapter;

  switch (config.mode) {
    case 'production':
    case 'development':
      browserAutomation = new PlaywrightAutomationAdapter(
        {
          headless: browserModeConfig.mode === 'headless',
          mode: adapterMode,
          userDataDir: sessionDataPath,
          baseUrl: fixtureMode ? 'http://localhost:3456' : '',
        },
        logger.child({ adapter: 'Playwright', mode: adapterMode }),
        browserModeConfigLoader
      );
      break;

    case 'test':
    default:
      if (fixtureMode) {
        browserAutomation = new PlaywrightAutomationAdapter(
          {
            headless: browserModeConfig.mode === 'headless',
            timeout: config.defaultTimeout ?? 10_000,
            baseUrl: 'http://localhost:3456',
            mode: 'real',
            userDataDir: sessionDataPath,
          },
          logger.child({ adapter: 'Playwright', mode: 'real' }),
          browserModeConfigLoader
        );
      } else {
        browserAutomation = new MockBrowserAutomationAdapter();
      }
      break;
  }

  container.registerInstance<IBrowserAutomation>(
    DI_TOKENS.BrowserAutomation,
    browserAutomation
  );

  // Checkout Service (singleton, backed by browser automation)
  container.registerInstance<CheckoutServicePort>(
    DI_TOKENS.CheckoutService,
    browserAutomation as unknown as CheckoutServicePort
  );

  // Automation Engine (singleton)
  const sessionRepository = container.resolve<SessionRepositoryPort>(DI_TOKENS.SessionRepository);
  let automationEngine: AutomationEnginePort;

  if (fixtureMode) {
    automationEngine = new AutomationEngineAdapter(
      browserAutomation,
      sessionRepository
    );
  } else {
    automationEngine = new MockAutomationEngineAdapter(
      browserAutomation,
      sessionRepository
    );
  }

  container.registerInstance<AutomationEnginePort>(
    DI_TOKENS.AutomationEngine,
    automationEngine
  );

  // Fixture Server (singleton, nullable)
  if (fixtureMode) {
    container.registerInstance(DI_TOKENS.FixtureServer, new FixtureServer());
  }

  // Use Cases - create singleton instance directly
  const startAutomationUseCase = new StartAutomationSessionUseCase(
    automationEngine,
    browserAutomation,
    sessionRepository,
    logger
  );
  container.registerInstance(DI_TOKENS.StartAutomationUseCase, startAutomationUseCase);

  // Authentication-related use cases (only if adapter supports it)
  if (browserAutomation instanceof PlaywrightAutomationAdapter && !fixtureMode) {
    const authService = browserAutomation as AuthenticationServicePort;

    container.registerInstance(
      DI_TOKENS.CheckAuthenticationUseCase,
      new CheckAuthenticationUseCase(authService, logger)
    );

    container.registerInstance(
      DI_TOKENS.InitiateLoginUseCase,
      new InitiateLoginUseCase(authService, logger)
    );

    container.registerInstance(
      DI_TOKENS.ClearSessionUseCase,
      new ClearSessionUseCase(authService, logger)
    );

    container.registerInstance<AuthenticationServicePort>(
      DI_TOKENS.AuthenticationService,
      authService
    );
  }

  // Overlay Sync Service - create singleton instance directly
  const lifecycleEmitter: IAutomationLifecycleEmitter = {
    onLifecycle: (cb) => {
      if ('onLifecycle' in browserAutomation && typeof (browserAutomation as { onLifecycle?: unknown }).onLifecycle === 'function') {
        (browserAutomation as IAutomationLifecycleEmitter).onLifecycle(cb);
      }
    },
    offLifecycle: (cb) => {
      if ('offLifecycle' in browserAutomation && typeof (browserAutomation as { offLifecycle?: unknown }).offLifecycle === 'function') {
        (browserAutomation as IAutomationLifecycleEmitter).offLifecycle(cb);
      }
    },
  };
  const publisher = {
    publish: async (event: unknown) => {
      try {
        logger.debug?.('OverlaySyncPublisher.publish', { event });
      } catch {
        // swallow
      }
    },
  };
  const overlaySyncService = new OverlaySyncService({
    lifecycleEmitter,
    publisher,
    logger,
  });
  container.registerInstance(DI_TOKENS.OverlaySyncPort, overlaySyncService);

  logger.info('DI container configured', {
    automationMode: config.mode,
    sessionRepositoryType: 'InMemorySessionRepository',
    browserAutomationType: browserAutomation.constructor.name,
  });
}

/**
 * Reset the container (for testing)
 */
export function resetDIContainer(): void {
  container.clearInstances();
}

/**
 * Get the TSyringe container instance
 */
export function getDIContainer() {
  return container;
}