import {View} from 'aurelia-templating';
import {Aurelia} from 'aurelia-framework';

export const StageComponent = {
  withResources(resources): ComponentTester {
    return new ComponentTester().withResources(resources);
  }
};

export class ComponentTester {
  bind: (bindingContext: any) => void;
  attached: () => void;
  unbind: () => void;
  element: Element;
  viewModel: any;
  configure = aurelia => aurelia.use.standardConfiguration();
  _html: string;
  _resources: string | string[] = [];
  _bindingContext: any;
  _rootView: View;

  bootstrap(configure: (aurelia: Aurelia) => void) {
    this.configure = configure;
  }

  withResources(resources: string | string[]): ComponentTester {
    this._resources = resources;
    return this;
  }

  inView(html: string): ComponentTester {
    this._html = html;
    return this;
  }

  boundTo(bindingContext: any): ComponentTester {
    this._bindingContext = bindingContext;
    return this;
  }

  manuallyHandleLifecycle(): ComponentTester {
    this._prepareLifecycle();
    return this;
  }

  create(bootstrap: (aurelia: Aurelia) => Promise<void>): Promise<void> {
    return bootstrap(aurelia => {
      return Promise.resolve(this.configure(aurelia)).then(() => {
        if (this._resources) {
          aurelia.use.globalResources(this._resources);
        }
        return aurelia.start().then(a => {
          this.host = document.createElement('div');
          this.host.innerHTML = this._html;
          document.body.appendChild(this.host);
          aurelia.enhance(this._bindingContext, this.host);
          this._rootView = aurelia.root;
          this.element = this.host.firstElementChild;
          if (aurelia.root.controllers.length) {
            this.viewModel = aurelia.root.controllers[0].viewModel;
          }
          return new Promise(resolve => setTimeout(() => resolve(), 0));
        });
      });
    });
  }

  dispose() {
    if (this.host === undefined || this._rootView === undefined) {
        throw new Error(
          'Cannot call ComponentTester.dispose() before ComponentTester.create()'
        );
    }
    this._rootView.detached();
    this._rootView.unbind();
    return this.host.parentNode.removeChild(this.host);
  }
  
  _prepareLifecycle() {
    // bind
    const bindPrototype = View.prototype.bind;
    View.prototype.bind = () => {};
    this.bind = bindingContext => new Promise(resolve => {
      View.prototype.bind = bindPrototype;
      if (bindingContext !== undefined) {
        this._bindingContext = bindingContext;
      }
      this._rootView.bind(this._bindingContext);
      setTimeout(() => resolve(), 0);
    });

    // attached
    const attachedPrototype = View.prototype.attached;
    View.prototype.attached = () => {};
    this.attached = () => new Promise(resolve => {
      View.prototype.attached = attachedPrototype;
      this._rootView.attached();
      setTimeout(() => resolve(), 0);
    });

    // detached
    this.detached = () => new Promise(resolve => {
      this._rootView.detached();
      setTimeout(() => resolve(), 0);
    });

    // unbind
    this.unbind = () => new Promise(resolve => {
      this._rootView.unbind();
      setTimeout(() => resolve(), 0);
    });
  }
}

@@ -1313,32 +1313,41 @@
 ces);%0A        %7D%0A
+        %0A
         return a
@@ -1369,24 +1369,24 @@
 then(a =%3E %7B%0A
-
           th
@@ -1466,24 +1466,25 @@
 this._html;%0A
+%0A
           do
@@ -1511,32 +1511,33 @@
 ild(this.host);%0A
+%0A
           aureli
@@ -1522,32 +1522,39 @@
 st);%0A%0A          
+return 
 aurelia.enhance(
@@ -1585,18 +1585,32 @@
 is.host)
-;%0A
+.then(() =%3E %7B%0A  
         
@@ -1648,24 +1648,26 @@
 ;%0A          
+ 
+ 
 this.element
@@ -1698,16 +1698,19 @@
 tChild;%0A
+%0A  
         
@@ -1758,24 +1758,26 @@
             
+  
 this.viewMod
@@ -1822,34 +1822,39 @@
 odel;%0A          
+ 
+ 
 %7D%0A
+%0A  
           return
@@ -1910,16 +1910,30 @@
 ), 0));%0A
+          %7D);%0A
         
@@ -2284,18 +2284,16 @@
 t);%0A  %7D%0A
-  
 %0A  _prep

handle enhance promise