'use strict';
describe('navigation', function () {
  var elt,
      $compile,
      $rootScope,
      $router,
      $compileProvider;
  beforeEach(function () {
    module('ng');
    module('ngComponentRouter');
    module(function (_$compileProvider_) {
      $compileProvider = _$compileProvider_;
    });
    inject(function (_$compile_, _$rootScope_, _$router_) {
      $compile = _$compile_;
      $rootScope = _$rootScope_;
      $router = _$router_;
    });
    registerComponent('userCmp', {
      template: '
hello {{userCmp.$routeParams.name}}
'
    });
    registerComponent('oneCmp', {
      template: '{{oneCmp.number}}
',
      controller: function () {this.number = 'one'}
    });
    registerComponent('twoCmp', {
      template: '{{twoCmp.number}}
',
      controller: function () {this.number = 'two'}
    });
  });
  it('should work in a simple case', function () {
    compile('');
    $router.config([
      { path: '/', component: 'oneCmp' }
    ]);
    $router.navigateByUrl('/');
    $rootScope.$digest();
    expect(elt.text()).toBe('one');
  });
  it('should navigate between components with different parameters', function () {
    $router.config([
      { path: '/user/:name', component: 'userCmp' }
    ]);
    compile('');
    $router.navigateByUrl('/user/brian');
    $rootScope.$digest();
    expect(elt.text()).toBe('hello brian');
    $router.navigateByUrl('/user/igor');
    $rootScope.$digest();
    expect(elt.text()).toBe('hello igor');
  });
  it('should reuse a parent when navigating between child components with different parameters', function () {
    var instanceCount = 0;
    function ParentController() {
      instanceCount += 1;
    }
    registerComponent('parentCmp', {
      template: 'parent {  }',
      $routeConfig: [
        { path: '/user/:name', component: 'userCmp' }
      ],
      controller: ParentController
    });
    $router.config([
      { path: '/parent/...', component: 'parentCmp' }
    ]);
    compile('');
    $router.navigateByUrl('/parent/user/brian');
    $rootScope.$digest();
    expect(instanceCount).toBe(1);
    expect(elt.text()).toBe('parent { hello brian }');
    $router.navigateByUrl('/parent/user/igor');
    $rootScope.$digest();
    expect(instanceCount).toBe(1);
    expect(elt.text()).toBe('parent { hello igor }');
  });
  it('should work with nested outlets', function () {
    registerComponent('childCmp', {
      template: '',
      $routeConfig: [
        { path: '/b', component: 'oneCmp' }
      ]
    });
    $router.config([
      { path: '/a/...', component: 'childCmp' }
    ]);
    compile('');
    $router.navigateByUrl('/a/b');
    $rootScope.$digest();
    expect(elt.text()).toBe('outer { inner { one } }');
  });
  // TODO: fix this
  xit('should work with recursive nested outlets', function () {
    registerComponent('recurCmp', {
      template: '',
      $routeConfig: [
        { path: '/recur', component: 'recurCmp' },
        { path: '/end', component: 'oneCmp' }
      ]});
    $router.config([
      { path: '/recur', component: 'recurCmp' },
      { path: '/', component: 'oneCmp' }
    ]);
    compile('');
    $router.navigateByUrl('/recur/recur/end');
    $rootScope.$digest();
    expect(elt.text()).toBe('root { one }');
  });
  it('should change location path', inject(function ($location) {
    $router.config([
      { path: '/user', component: 'userCmp' }
    ]);
    compile('');
    $router.navigateByUrl('/user');
    $rootScope.$digest();
    expect($location.path()).toBe('/user');
  }));
  it('should change location to the canonical route', inject(function ($location) {
    compile('');
    $router.config([
      { path: '/',     redirectTo: '/user' },
      { path: '/user', component:  'userCmp' }
    ]);
    $router.navigateByUrl('/');
    $rootScope.$digest();
    expect($location.path()).toBe('/user');
  }));
  it('should change location to the canonical route with nested components', inject(function ($location) {
    registerComponent('childRouter', {
      template: '',
      $routeConfig: [
        { path: '/old-child', redirectTo: '/new-child' },
        { path: '/new-child', component: 'oneCmp'},
        { path: '/old-child-two', redirectTo: '/new-child-two' },
        { path: '/new-child-two', component: 'twoCmp'}
      ]
    });
    $router.config([
      { path: '/old-parent', redirectTo: '/new-parent' },
      { path: '/new-parent/...', component:  'childRouter' }
    ]);
    compile('');
    $router.navigateByUrl('/old-parent/old-child');
    $rootScope.$digest();
    expect($location.path()).toBe('/new-parent/new-child');
    expect(elt.text()).toBe('inner { one }');
    $router.navigateByUrl('/old-parent/old-child-two');
    $rootScope.$digest();
    expect($location.path()).toBe('/new-parent/new-child-two');
    expect(elt.text()).toBe('inner { two }');
  }));
  it('should navigate when the location path changes', inject(function ($location) {
    $router.config([
      { path: '/one', component: 'oneCmp' }
    ]);
    compile('');
    $location.path('/one');
    $rootScope.$digest();
    expect(elt.text()).toBe('one');
  }));
  it('should expose a "navigating" property on $router', inject(function ($q) {
    var defer;
    registerComponent('pendingActivate', {
      $canActivate: function () {
        defer = $q.defer();
        return defer.promise;
      }
    });
    $router.config([
      { path: '/pending-activate', component: 'pendingActivate' }
    ]);
    compile('');
    $router.navigateByUrl('/pending-activate');
    $rootScope.$digest();
    expect($router.navigating).toBe(true);
    defer.resolve();
    $rootScope.$digest();
    expect($router.navigating).toBe(false);
  }));
  function registerComponent(name, options) {
    var controller = options.controller || function () {};
    ['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
      if (options[hookName]) {
        controller.prototype[hookName] = options[hookName];
      }
    });
    function factory() {
      return {
        template: options.template || '',
        controllerAs: name,
        controller: controller
      };
    }
    if (options.$canActivate) {
      factory.$canActivate = options.$canActivate;
    }
    if (options.$routeConfig) {
      factory.$routeConfig = options.$routeConfig;
    }
    $compileProvider.directive(name, factory);
  }
  function compile(template) {
    elt = $compile('' + template + '
')($rootScope);
    $rootScope.$digest();
    return elt;
  }
});