import test from 'ava'
import sinon from 'sinon'
import later from 'later'

import router from './router'

// Tests -- actions

test('should exist', (t) => {
  t.is(typeof router, 'function')
})

test('should return status noaction when no action', async (t) => {
  const action = null
  const sources = {}

  const ret = await router(action, {sources})

  t.deepEqual(ret, {status: 'noaction'})
})

test('should return null when unknown action', async (t) => {
  const action = {type: 'UNKNOWN', source: 'entries'}
  const sources = {}
  const actions = {}

  const ret = await router(action, {sources, actions})

  t.deepEqual(ret, {status: 'noaction'})
})

test('should route to GET action', async (t) => {
  const items = [{id: 'ent1', type: 'entry'}]
  const action = {
    type: 'GET',
    source: 'entries',
    payload: {id: 'ent1', type: 'entry'}
  }
  const actions = {
    'GET': async () => ({status: 'ok', data: items})
  }

  const ret = await router(action, {actions})

  t.is(ret.status, 'ok')
  t.deepEqual(ret.data, items)
})

test('should call action handler with sources, types, workers, and dispatch', async (t) => {
  const action = {
    type: 'GET'
  }
  const getHandler = sinon.stub().returns(Promise.resolve({status: 'ok'}))
  const actions = {'GET': getHandler}
  const sources = {}
  const types = {}
  const workers = {}
  const dispatch = () => {}

  await router(action, {actions, sources, types, workers, dispatch})

  t.true(getHandler.calledOnce)
  const resources = getHandler.args[0][1]
  t.is(resources.sources, sources)
  t.is(resources.types, types)
  t.is(resources.workers, workers)
  t.is(resources.dispatch, dispatch)
})

// Tests -- queue

test('should add action to queue', async (t) => {
  const action = {
    type: 'SET',
    source: 'store',
    payload: {},
    queue: true
  }
  const queue = sinon.stub().returns(Promise.resolve(true))
  const actions = {
    'SET': async () => ({status: 'ok'})
  }

  const ret = await router(action, {queue, actions})

  t.deepEqual(ret, {status: 'queued'})
  t.true(queue.calledOnce)
  const pushed = queue.args[0][0]
  t.truthy(pushed)
  t.is(pushed.type, 'SET')
  t.is(pushed.source, 'store')
  t.deepEqual(pushed.payload, {})
  t.false(pushed.queue)
  const timestamp = queue.args[0][1]
  t.is(timestamp, null)
})

test('should schedule action', async (t) => {
  const action = {
    type: 'SET',
    source: 'store',
    payload: {},
    queue: new Date('2017-09-01T09:00:00Z')
  }
  const queue = sinon.stub().returns(Promise.resolve(true))
  const actions = {
    'SET': async () => ({status: 'ok'})
  }

  const ret = await router(action, {queue, actions})

  t.deepEqual(ret, {status: 'queued'})
  t.true(queue.calledOnce)
  const pushed = queue.args[0][0]
  t.false(pushed.queue)
  const timestamp = queue.args[0][1]
  t.is(new Date(timestamp).getTime(), 1504256400000)
})

test('should return error when queueing fails', async (t) => {
  const action = {
    type: 'SET',
    source: 'store',
    payload: {},
    queue: true
  }
  const queue = sinon.stub().returns(Promise.resolve(false))
  const actions = {
    'SET': async () => ({status: 'ok'})
  }

  const ret = await router(action, {queue, actions})

  t.deepEqual(ret, {status: 'error'})
})

test('should reschedule action', async (t) => {
  const schedule = {schedules: [{h: [2]}]}
  const action = {
    type: 'RUN',
    queue: false,
    schedule,
    payload: {worker: 'sync'}
  }
  const nextTime = later.schedule(schedule).next().getTime()
  const queue = sinon.stub().returns(Promise.resolve(true))
  const run = sinon.stub().returns(Promise.resolve({status: 'ok'}))
  const actions = {'RUN': run}

  const ret = await router(action, {queue, actions})

  t.deepEqual(ret, {status: 'ok'})
  t.true(run.calledOnce)
  t.true(queue.calledOnce)
  const reaction = queue.args[0][0]
  const timestamp = queue.args[0][1]
  t.deepEqual(reaction, action)
  t.truthy(timestamp)
  t.is(timestamp.getTime(), nextTime)
})

test('should not reschedule when schedule is ended', async (t) => {
  const action = {
    type: 'RUN',
    queue: false,
    schedule: {schedules: [{Y_b: [2015]}]},
    payload: {worker: 'sync'}
  }
  const queue = sinon.stub().returns(Promise.resolve(true))
  const actions = {'RUN': async () => ({status: 'ok'})}

  await router(action, {queue, actions})

  t.false(queue.called)
})

@@ -781,24 +781,41 @@
 ype: 'GET',%0A
+    payload: %7B%0A  
     source: 
@@ -829,26 +829,18 @@
 s',%0A    
-payload: %7B
+  
 id: 'ent
@@ -834,32 +834,38 @@
      id: 'ent1',
+%0A     
  type: 'entry'%7D%0A
@@ -862,16 +862,21 @@
  'entry'
+%0A    
 %7D%0A  %7D%0A  
@@ -1161,37 +1161,32 @@
 const action = %7B
-%0A    
 type: 'GET'%0A  %7D%0A
@@ -1180,19 +1180,16 @@
 e: 'GET'
-%0A  
 %7D%0A  cons
@@ -1768,32 +1768,49 @@
    type: 'SET',%0A
+    payload: %7B%0A  
     source: 'sto
@@ -1812,32 +1812,21 @@
  'store'
-,
 %0A    
-payload: %7B
 %7D,%0A    q
@@ -2169,39 +2169,8 @@
 T')%0A
-  t.is(pushed.source, 'store')%0A
   t.
@@ -2196,16 +2196,31 @@
 yload, %7B
+source: 'store'
 %7D)%0A  t.f
@@ -2377,32 +2377,49 @@
    type: 'SET',%0A
+    payload: %7B%0A  
     source: 'sto
@@ -2421,32 +2421,21 @@
  'store'
-,
 %0A    
-payload: %7B
 %7D,%0A    q
@@ -2969,24 +2969,41 @@
 ype: 'SET',%0A
+    payload: %7B%0A  
     source: 
@@ -3009,32 +3009,21 @@
  'store'
-,
 %0A    
-payload: %7B
 %7D,%0A    q

Correct wrong action format in test