import type { ConsolaInstance } from "consola";
import type { IDisposable, IMountable } from "@/interfaces/service";
import { createLogger } from "@/utils/logger";

export class ServiceError extends Error {
	constructor(message: string, options?: ErrorOptions) {
		super(message, options);
		this.name = "ServiceError";
	}
}

export type ServiceToken<T extends BaseService = BaseService> = {
	prototype: T;
};

export interface ServiceContainer {
	get<T extends BaseService>(token: ServiceToken<T>): T;
	getByName<T extends BaseService>(name: string): T;
}

export abstract class BaseService implements IMountable, IDisposable {
	public readonly name: string;

	public readonly logger: ConsolaInstance;

	public abstract isRunning: boolean;

	private _container: ServiceContainer | null = null;

	protected constructor(name: string) {
		if (!name) {
			throw new ServiceError(
				"A service MUST define service name in constructor.",
			);
		}

		this.name = name;
		this.logger = createLogger(this.name);
	}

	public attachContainer(container: ServiceContainer): void {
		this._container = container;
	}

	public detachContainer(): void {
		this._container = null;
	}

	protected get services(): ServiceContainer {
		if (!this._container) {
			throw new ServiceError(
				"Service container is not attached. Ensure the service is registered before use.",
			);
		}
		return this._container;
	}

	public hasContainer(): boolean {
		return this._container !== null;
	}

	public abstract mount(): Promise<void>;
	public abstract dispose(): Promise<void>;
}
