import { Descriptor, PartialServiceMap, ILifetime, ContainerKeys } from "../interfaces";
import { ActivationContext } from "../ActivationContext";
import { each } from "../../safe";
import { DependencyOptions, LazyDependencyOptions, Resolver } from "./interfaces";

export interface DescriptorImplArgs<S extends object, T> {
    lifetime: ILifetime;

    factory: (resolve: Resolver<S>) => T;

    cleanup?: (item: T) => void;

    overrides?: PartialServiceMap<S>;
}

export class DescriptorImpl<S extends object, T> implements Descriptor<S, T> {

    private readonly _overrides?: PartialServiceMap<S>;

    private readonly _lifetime: ILifetime;

    private readonly _factory: (resolve: Resolver<S>) => T;

    private readonly _cleanup?: (item: T) => void;

    constructor(args: DescriptorImplArgs<S, T>) {
        this._lifetime = args.lifetime;
        this._factory = args.factory;
        if (args.cleanup)
            this._cleanup = args.cleanup;
        if (args.overrides)
            this._overrides = args.overrides;
    }

    activate(context: ActivationContext<S>): T {

        if (this._lifetime.has())
            return this._lifetime.get();

        this._lifetime.initialize(context);

        if (this._overrides)
            each(this._overrides, (v, k) => context.register(k, v));

        const resolve = (name: ContainerKeys<S>, opts?: DependencyOptions | LazyDependencyOptions) => {
            if (opts && "lazy" in opts && opts.lazy) {
                const c2 = context.clone();
                return () => {
                    return opts.optional ? c2.resolve(name, opts.default) : c2.resolve(name);
                };
            } else {
                return opts && opts.optional ? context.resolve(name, opts.default) : context.resolve(name);
            }
        };

        const instance = this._factory.call(undefined, resolve);

        this._lifetime.store(instance, this._cleanup);

        return instance;
    }

    toString() {
        return `[object DescriptorImpl, lifetime=${this._lifetime}]`;
    }
}
