import { DriveListItem, DriverContract } from './types/drive.js';
// import * as fsExtra from 'fs-extra';
import { DirectoryListingContract } from './types/drive.js';

// class AsyncIterableArray<T> implements AsyncIterable<T> {
export class AsyncIterableArray<SpecificDriver extends DriverContract, T extends DriveListItem>
    implements DirectoryListingContract<SpecificDriver, T>
{
    public driver: SpecificDriver;
    private generator: () => AsyncGenerator<T, void, unknown>;
    // private generator: () => AsyncGenerator<T, void, unknown>;
    private chain: any[];

    constructor(driver: SpecificDriver, generator: () => AsyncGenerator<T, void, unknown>) {
        // constructor(driver: SpecificDriver, listing: T) {
        this.driver = driver;
        this.generator = generator;
        /**
         * Functions chain to be executed for transforming generated listing iterable
         */
        this.chain = [];
    }

    /**
     * Convert directory listing to array.
     */
    // public async toArray(): Promise<T[]> {
    //     const arr = [];
    //     for await (const item of this.toIterable()) {
    //         arr.push(item);
    //     }
    //     return arr;
    // }
    async toArray(): Promise<T[]> {
        const arr: T[] = [];
        for await (const item of this) {
            arr.push(item);
        }
        return arr;
    }

    /**
     * A method that returns the default async iterator for an object.
     */
    public async *[Symbol.asyncIterator](): AsyncIterableIterator<T> {
        //  yield* this.toIterable();
        for await (const item of this.generator.call(this.driver)) {
            yield item;
        }
        // yield 1
        // // await something()
        // yield 2
        // // await somethingElse()
        // yield 3
    }

    /**
     * Get the final async iterable after passing directory listing through chain of piping functions modifying the output.
     */
    public toIterable(): AsyncIterable<T> {
        const generator = this.generator.call(this.driver);
        const iterable = this.chain.reduce((prevIterable, currentIterable) => {
            return currentIterable.call(this.driver, prevIterable);
        }, generator);

        return iterable;
    }
}
