import { forwardRef, Inject, Module, Optional, type OnApplicationShutdown } from '@nestjs/common';
import { createQueue, type QueueHandle } from '@mosaicstack/queue';
import type { MosaicConfig } from '@mosaicstack/config';
import { MOSAIC_CONFIG } from '../config/config.module.js';
import { ChatModule } from '../chat/chat.module.js';
import { GCModule } from '../gc/gc.module.js';
import { McpClientModule } from '../mcp-client/mcp-client.module.js';
import { ReloadModule } from '../reload/reload.module.js';
import { CommandAuthorizationService } from './command-authorization.service.js';
import { CommandExecutorService } from './command-executor.service.js';
import { CommandRegistryService } from './command-registry.service.js';
import { CommandRuntimeApprovalVerifier } from './runtime-approval-verifier.js';
import { COMMANDS_REDIS } from './commands.tokens.js';

const COMMANDS_QUEUE_HANDLE = 'COMMANDS_QUEUE_HANDLE';

@Module({
  imports: [
    GCModule,
    McpClientModule,
    forwardRef(() => ReloadModule),
    forwardRef(() => ChatModule),
  ],
  providers: [
    {
      provide: COMMANDS_QUEUE_HANDLE,
      useFactory: (config: MosaicConfig | null): QueueHandle | null => {
        // On Local tier there is no Redis — skip the ioredis connection.
        // CommandExecutorService falls back to no-cache for /provider login on local.
        if (config?.queue?.type === 'local') return null;
        return createQueue();
      },
      inject: [MOSAIC_CONFIG],
    },
    {
      provide: COMMANDS_REDIS,
      useFactory: (handle: QueueHandle | null) => handle?.redis ?? null,
      inject: [COMMANDS_QUEUE_HANDLE],
    },
    CommandRegistryService,
    CommandAuthorizationService,
    CommandRuntimeApprovalVerifier,
    CommandExecutorService,
  ],
  exports: [
    CommandRegistryService,
    CommandAuthorizationService,
    CommandRuntimeApprovalVerifier,
    CommandExecutorService,
  ],
})
export class CommandsModule implements OnApplicationShutdown {
  constructor(
    @Optional()
    @Inject(COMMANDS_QUEUE_HANDLE)
    private readonly handle: QueueHandle | null,
  ) {}

  async onApplicationShutdown(): Promise<void> {
    await this.handle?.close().catch(() => {});
  }
}
