import {
	AppConfigDataClient,
	GetLatestConfigurationCommand,
	StartConfigurationSessionCommand,
} from '@aws-sdk/client-appconfigdata'
import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common'
import { EventEmitter2 } from '@nestjs/event-emitter'
import { APPCONFIG_CONFIG } from '../../config'
import { InjectAws } from '@backend/aws'

@Injectable()
export class AppConfigService implements OnModuleInit, OnModuleDestroy {
	private readonly logger = new Logger(this.constructor.name)

	private pollInterval: NodeJS.Timeout
	private configurationToken?: string
	private config: Record<string, any> = {}

	constructor(
		@InjectAws(AppConfigDataClient)
		private readonly _appConfigClient: AppConfigDataClient,
		private readonly _eventEmitter: EventEmitter2,
	) {}

	async onModuleInit() {
		await this.startSession()
		await this.fetchConfig()
		this.startPolling()
	}

	onModuleDestroy() {
		if (this.pollInterval) {
			clearInterval(this.pollInterval)
		}
	}

	get<T>(key: string, defaultValue: T): T {
		return this.config[key] ?? defaultValue
	}

	getAll(): Record<string, unknown> {
		return this.config
	}

	private async startSession() {
		try {
			const command = new StartConfigurationSessionCommand({
				ApplicationIdentifier: APPCONFIG_CONFIG.APPLICATION,
				EnvironmentIdentifier: APPCONFIG_CONFIG.ENVIRONMENT,
				ConfigurationProfileIdentifier: APPCONFIG_CONFIG.CONFIGURATION,
			})
			const response = await this._appConfigClient.send(command)
			this.configurationToken = response.InitialConfigurationToken
		} catch (error) {
			this.logger.error('Failed to start AppConfig session', error)
		}
	}

	private startPolling() {
		this.pollInterval = setInterval(() => this.fetchConfig(), APPCONFIG_CONFIG.POLL_INTERVAL)
	}

	private async fetchConfig() {
		if (!this.configurationToken) await this.startSession()
		if (!this.configurationToken) return

		try {
			const command = new GetLatestConfigurationCommand({
				ConfigurationToken: this.configurationToken,
			})

			const response = await this._appConfigClient.send(command)
			this.configurationToken = response.NextPollConfigurationToken

			if (response.Configuration) {
				const rawConfig = Buffer.from(response.Configuration).toString('utf8')
				if (rawConfig) {
					const updatedConfig = JSON.parse(rawConfig)

					if (updatedConfig !== this.config) {
						this.logger.log({ message: 'Configuration updated from AppConfig', config: updatedConfig })
						this.config = updatedConfig
						this._eventEmitter.emit('app-config.updated', this.config)
					}
				}
			}
		} catch (error) {
			this.logger.error('Failed to fetch configuration from AppConfig', error)
		}
	}
}
