import { InjectionToken, ModuleWithProviders, NgModule } from '@angular/core';
import * as localForage from 'localforage';

import { CollectionFactory } from './collection';
import { createLocalResourceFactory } from './local';
import { createResourceFactory } from './model';
import { createQueryFactory } from './query';
import { createServerResourceFactory } from './server';

import { CollectionFactory as ICollectionFactory,
         CollectionStatic,
         DatabaseFactory,
         LocalForage,
         LocalResourceFactory,
         Model,
         Plugin,
         QueryFactory,
         ResourceFactory,
         ServerResourceFactory,
         SocketFactory } from './types';

import './declarations.d';

export * from './types';
export { Deferred } from './utils';

export const SERVER_RESOURCE_FACTORY = new InjectionToken<ServerResourceFactory>('server resource factory');
export const LOCAL_RESOURCE_FACTORY = new InjectionToken<LocalResourceFactory>('local resource factory');
export const RESOURCE_FACTORY = new InjectionToken<ResourceFactory<Model>>('resource factory');
export const QUERY_FACTORY = new InjectionToken<QueryFactory>('query factory');
export const COLLECTION_FACTORY = new InjectionToken<CollectionStatic<Model>>('collection factory');
export const SOCKET_FACTORY = new InjectionToken<SocketFactory>('socket factory');
export const DATABASE_FACTORY = new InjectionToken<DatabaseFactory>('database factory');

export interface OrtooResourceOpts {
  localForage?: LocalForage;
  plugins?: Array<Plugin<Model>>;
  pStorageMaxLen?: number;
}

@NgModule()
export class OrtooResourceModule {
  public static forOptions(opts: OrtooResourceOpts): ModuleWithProviders {
    return {
      ngModule: OrtooResourceModule,
      providers: [
        {provide: SERVER_RESOURCE_FACTORY, useFactory: createServerResourceFactory, deps: [SOCKET_FACTORY]},
        {provide: QUERY_FACTORY, useFactory: createQueryFactory, deps: [SOCKET_FACTORY]},
        {provide: COLLECTION_FACTORY, useValue: CollectionFactory },
        {
          deps: [SERVER_RESOURCE_FACTORY, QUERY_FACTORY, COLLECTION_FACTORY, DATABASE_FACTORY],
          provide: LOCAL_RESOURCE_FACTORY,
          useFactory: (srf: ServerResourceFactory,
                       qf: QueryFactory,
                       cf: ICollectionFactory<Model>,
                       dbf: DatabaseFactory) => {
            const lf = opts.localForage || localForage;
            return createLocalResourceFactory(srf, qf, cf, dbf, lf);
          },
        },
        {
          deps: [LOCAL_RESOURCE_FACTORY],
          provide: RESOURCE_FACTORY,
          useFactory: (lrf: LocalResourceFactory) => {
            const lf = opts.localForage || localForage;
            const plugins = opts.plugins || [];
            return createResourceFactory(lrf, lf, plugins, opts.pStorageMaxLen);
          },
        },
      ],
    };
  }
}
