import { pathToFileURL } from 'url';
import * as path from 'path';
import * as fs from 'fs';
import { InvalidMiddlewareException, InvalidRouteException } from '../exceptions';
import { Route, HttpHandler, Middleware } from '../http';
import Registry from './Registry';

class DynamicRegistry extends Registry {
	private readonly routesDir?: string;
	private readonly middlewaresDir?: string;

	public constructor(routesDir?: string, middlewaresDir?: string) {
		super();
		this.routesDir = routesDir;
		this.middlewaresDir = middlewaresDir;
	}

	protected async registerRoutes(): Promise<Registry> {
		if(this.routesDir == null)
			return this;

		const routesDir: string = this.routesDir;
		const files = fs.readdirSync(routesDir, { recursive: true, encoding: 'utf8' });

		for(const file of files) {
			if(/\.js$/.test(file)) {
				const fileUrl = pathToFileURL(path.join(routesDir, file)).toString();
				const RouteClass = await import(fileUrl).then(mod => mod.default?.default ?? mod.default ?? mod);
				if(RouteClass.prototype instanceof Route) {
					this.registerHttpHandler(<HttpHandler>new RouteClass(this.getServer()));
				} else throw new InvalidRouteException(fileUrl);
			}
		}
		return this;
	}

	protected async registerMiddlewares(): Promise<Registry> {
		if(this.middlewaresDir == null)
			return this;

		const middlewaresDir: string = this.middlewaresDir;
		const files = fs.readdirSync(middlewaresDir, { recursive: true, encoding: 'utf8' });
		
		for(const file of files) {
			if(/\.js$/.test(file)) {
				const fileUrl = pathToFileURL(path.join(middlewaresDir, file)).toString();
				const MiddlewareClass = await import(fileUrl).then(mod => mod.default?.default ?? mod.default ?? mod);
				if(MiddlewareClass.prototype instanceof Middleware) {
					this.registerHttpHandler(<HttpHandler>new MiddlewareClass(this.getServer()));
				} else throw new InvalidMiddlewareException(fileUrl);
			}
		}

		return this;
	}	
}

export default DynamicRegistry;