import { createLogger } from "@/utils/logger";
import {
	type BaseService,
	type ServiceContainer,
	ServiceError,
	type ServiceToken,
} from "./BaseService";

export class Service implements ServiceContainer {
	private readonly logger = createLogger("Service");
	private readonly _registeredService: BaseService[] = [];
	private readonly _registryByName = new Map<string, BaseService>();
	private readonly _registryByToken = new Map<ServiceToken, BaseService>();
	private _runningService = 0;
	private _activationOrder: BaseService[] = [];

	public registerService(...services: BaseService[]): void {
		for (const service of services) {
			if (this._registryByName.has(service.name)) {
				this.logger.warn(
					`Service with name "${service.name}" is already registered. Skipping duplicate instance.`,
				);
				continue;
			}

			this._registeredService.push(service);
			this._registryByName.set(service.name, service);
			this._registryByToken.set(service.constructor as ServiceToken, service);
			service.attachContainer(this);
		}
	}

	public get<T extends BaseService>(token: ServiceToken<T>): T {
		const service = this._registryByToken.get(token);
		if (!service) {
			const tokenName = (token as { name?: string }).name ?? "<anonymous>";
			throw new ServiceError(
				`Service for token "${tokenName}" is not registered.`,
			);
		}
		return service as T;
	}

	public getByName<T extends BaseService>(name: string): T {
		const service = this._registryByName.get(name);
		if (!service) {
			throw new ServiceError(`Service with name "${name}" is not registered.`);
		}
		return service as T;
	}

	public async activateService(): Promise<void> {
		const activated: BaseService[] = [];
		for (const service of this._registeredService) {
			if (service.isRunning) {
				continue;
			}

			try {
				this.logger.debug(`Activating service ${service.name}`);
				await service.mount();
				this._runningService += 1;
				activated.push(service);
			} catch (error) {
				this.logger.error(
					`Error while activating service: ${service.name}`,
					error,
				);
				await this.rollbackActivated(activated);
				throw error;
			}
		}
		this._activationOrder = activated;
	}

	public async deactivateService(): Promise<void> {
		const toDeactivate = [...this._activationOrder].reverse();
		for (const service of toDeactivate) {
			try {
				this.logger.debug(`Deactivating service ${service.name}`);
				await service.dispose();
				if (this._runningService > 0) {
					this._runningService -= 1;
				}
			} catch (error) {
				this.logger.error(
					`Error while deactivating service: ${service.name}`,
					error,
				);
			}
		}
		this._activationOrder = [];
	}

	private async rollbackActivated(services: BaseService[]): Promise<void> {
		for (const service of [...services].reverse()) {
			try {
				this.logger.debug(`Rolling back service activation ${service.name}`);
				await service.dispose();
				if (this._runningService > 0) {
					this._runningService -= 1;
				}
			} catch (error) {
				this.logger.error(
					`Error while rolling back service: ${service.name}`,
					error,
				);
			}
		}
	}
}
