import { Inject, Injectable, InjectionToken, Optional, Provider, SkipSelf, Type } from '@angular/core';
import { HypermediaField } from 'first-npm-package-nicule/core';
import { InputConfiguration } from '../interfaces';

export function inputComponentResolver(parent: InputComponentResolver, inputConfiguration): InputComponentResolver {
    return new InputComponentResolver(parent, inputConfiguration);
}

export const INPUT_CONFIGURATION = new InjectionToken('INPUT_CONFIGURATION');

@Injectable()
export class InputComponentResolver {
    public static forInputs(inputsConfiguration: Array<InputConfiguration>): Provider {
        return [
            provideInpuConfigurations(inputsConfiguration),
            {
                provide: InputComponentResolver,
                useFactory: inputComponentResolver,
                deps: [[InputComponentResolver, new SkipSelf(), new Optional()], INPUT_CONFIGURATION]
            }
        ];
    }

    constructor(private parent: InputComponentResolver,
                @Inject(INPUT_CONFIGURATION) protected configuration: Array<InputConfiguration>) { }

    getInputBundleForName(name: string): Type<any> {
        const inputConfiguration = this.resolveComponentTypeByKey(name, 'name');

        if (inputConfiguration) {
            return inputConfiguration.component;
        }

        return this.parent && this.parent.getInputBundleForName(name);
    }

    getInputBundleForType(type: string): Type<any> {
        const inputConfiguration = this.resolveComponentTypeByKey(type, 'type');

        if (inputConfiguration) {
            return inputConfiguration.component;
        }

        return this.parent && this.parent.getInputBundleForType(type);
    }

    resolve(field: HypermediaField): Type<any> {
        return this.getInputBundleForName(field.name) ||
               this.getInputBundleForType(field.type);
    }

    resolveComponentTypeByKey(match: string, key: 'name' | 'type'): InputConfiguration {
        return this.configuration.find(configuration => {
            if (!configuration[key]) {
                return undefined;
            }
            if (configuration[key] instanceof RegExp) {
                return (configuration[key] as RegExp).test(match);
            }

            return configuration[key] === match;
        });
    }
}

export function provideInpuConfigurations(inputsConfiguration: Array<InputConfiguration>): any {
    return [
      { provide: INPUT_CONFIGURATION, multi: false, useValue: inputsConfiguration }
    ];
}
