import 'reflect-metadata';
import * as fs from 'fs';
import * as path from 'path';

export abstract class Detectable {
    constructor(public name: string) {}
}

export function autodetect<T extends Detectable>(options: { baseClass: new (...args: any[]) => T, location: string }) {
    return function (target: any, propertyKey: string) {
        const location = path.resolve(options.location);

        Reflect.defineMetadata('autodetect:options', options, target, propertyKey);

        Object.defineProperty(target, propertyKey, {
            get: function() {
                if (!this._loadedItems) {
                    this._loadedItems = loadItems(location, options.baseClass);
                }
                return this._loadedItems;
            },
            configurable: true,
            enumerable: true
        });
    };
}

function loadItems<T extends Detectable>(directoryPath: string, BaseClass: new (...args: any[]) => T): T[] {
    const items: T[] = [];

    const files = fs.readdirSync(directoryPath);

    for (const file of files) {
        if (file.endsWith('.ts') || file.endsWith('.js')) {
            const filePath = path.join(directoryPath, file);
            let module;
            try {
                module = require(filePath);
            } catch (error) {
                console.error(`Error loading module ${filePath}:`, error);
                continue;
            }

            for (const exportedItem of Object.values(module)) {
                if (typeof exportedItem === 'function' &&
                    exportedItem.prototype instanceof BaseClass) {
                    const ItemClass = exportedItem as new (name: string) => T;
                    items.push(new ItemClass(path.basename(file, path.extname(file))));
                }
            }
        }
    }

    return items;
}