/**
 * Module Dependencies
 */

var parse = require('../lib/parser');
var assert = require('better-assert');

/**
 * Some predefined dates
 */

var mon = new Date('May 13, 2013 01:30:00');

/**
 * Test parser
 */

/**
 * Minutes
 */

describe('minutes', function() {
  it('10m', function() {
    var date = parse('10m', mon);
    assert('1:40:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('10min', function() {
    var date = parse('10min', mon);
    assert('1:40:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('10 minutes', function() {
    var date = parse('10 minutes', mon);
    assert('1:40:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('10 minutes from now', function() {
    var date = parse('10 minutes from now', mon);
    assert('1:40:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('10 minutes starting tomorrow', function() {
    var date = parse('10 minutes starting tomorrow', mon);
    assert('1:40:00' == t(date));
    assert('5/14/13' == d(date));
  });
});

/**
 * Hours
 */

describe('hours', function() {
  it('in 5 hours', function() {
    var date = parse('in 5 hours', mon);
    assert('6:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('5 hours later', function() {
    var date = parse('5 hours later', mon);
    assert('6:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('in 5h', function() {
    var date = parse('in 5h', mon);
    assert('6:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('in 5hrs', function() {
    var date = parse('in 5hrs', mon);
    assert('6:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at 5am', function() {
    var date = parse('5am', mon);
    assert('5:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at 5pm', function() {
    var date = parse('5pm', mon);
    assert('17:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at5', function() {
    var date = parse('at5', mon);
    assert('5:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at 17', function() {
    var date = parse('at 17', mon);
    assert('17:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at 12:30', function() {
    var date = parse('at 12:30', mon);
    assert('12:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at 12.30', function() {
    var date = parse('at 12.30', mon);
    assert('12:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at 23:35', function() {
    var date = parse('at 23:35', mon);
    assert('23:35:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('at 0:30', function() {
    var date = parse('at 0:30', mon);
    assert('0:30:00' == t(date));
    assert('5/14/13' == d(date));
  });
});

/**
 * Days
 */

describe('days', function() {
  it('in 2 days', function() {
    var date = parse('in 2 days', mon);
    assert('1:30:00' == t(date));
    assert('5/15/13' == d(date));
  });

  it('in 2d', function() {
    var date = parse('in 2d', mon);
    assert('1:30:00' == t(date));
    assert('5/15/13' == d(date));
  });
});

/**
 * Dates
 */

describe('dates', function() {
  it('tuesday at 9am', function() {
    var date = parse('tuesday at 9am', mon);
    assert('9:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('monday at 9am', function() {
    var date = parse('monday at 9am', mon);
    assert('9:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('Monday at 9am', function() {
    var date = parse('Monday at 9am', mon);
    assert('9:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('monday at 9', function() {
    var date = parse('monday at 9', mon);
    assert('9:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('monday at 21', function() {
    var date = parse('monday at 21', mon);
    assert('21:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('monday at 1:00am', function() {
    var date = parse('monday at 1:00am', mon);
    assert('1:00:00' == t(date));
    assert('5/20/13' == d(date));
  });

  it('next monday at 1:00am', function() {
    var date = parse('next monday at 1:00am', mon);
    assert('1:00:00' == t(date));
    assert('5/20/13' == d(date));
  });

  it('last monday at 1:00am', function() {
    var date = parse('last monday at 1:00am', mon);
    assert('1:00:00' == t(date));
    assert('5/6/13' == d(date));
  });
});

/**
 * Tomorrow
 */

describe('tomorrow', function() {
  it('tomorrow at 3pm', function() {
    var date = parse('tomorrow at 3pm', mon);
    assert('15:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('tmr at 3pm', function() {
    var date = parse('tmr at 3pm', mon);
    assert('15:00:00' == t(date));
    assert('5/14/13' == d(date));
  });
});

/**
 * Yesterday
 */

describe('yesterday', function() {
  it('yesterday at 3pm', function() {
    var date = parse('yesterday at 3pm', mon);
    assert('15:00:00' == t(date));
    assert('5/12/13' == d(date));
  });

  it('ytd at 3pm', function() {
    var date = parse('ytd at 3pm', mon);
    assert('15:00:00' == t(date));
    assert('5/12/13' == d(date));
  });

  it('yesterday at 15', function() {
    var date = parse('yesterday at 15', mon);
    assert('15:00:00' == t(date));
    assert('5/12/13' == d(date));
  });

  it('yesterday at 12:30am', function() {
    var date = parse('yesterday at 12:30am', mon);
    assert('0:30:00' == t(date));
    assert('5/12/13' == d(date));
  });
});

/**
 * Tonight
 */

describe('tonight', function() {
  it('5pm tonight', function() {
    var date = parse('5pm tonight', mon);
    assert('17:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tonight at 5pm', function() {
    var date = parse('tonight at 5pm', mon);
    assert('17:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tonight at 5', function() {
    var date = parse('tonight at 5', mon);
    assert('17:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tonight at 5:30', function() {
    var date = parse('tonight at 5:30', mon);
    assert('17:30:00' == t(date));
    assert('5/13/13' == d(date));
  });
});

/**
 * Midnight
 */
describe('mightnight', function() {
  it('midnight', function() {
    var date = parse('midnight', mon);

    assert('0:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('tomorrow at midnight', function() {
    var date = parse('tomorrow at midnight', mon);
    assert('0:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('midnight (@ 1:30pm)', function() {
    var afternoon = new Date('May 13, 2013 13:30:00')
    var date = parse('midnight', afternoon);
    assert('0:00:00' == t(date));
    assert('5/14/13' == d(date));
  });
});

/**
 * Noon
 */

describe('noon', function() {
  it('noon', function() {
    var date = parse('noon', mon);
    assert('12:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tomorrow at noon', function() {
    var date = parse('tomorrow at noon', mon);
    assert('12:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('noon (@ 1:30pm)', function() {
    var afternoon = new Date('May 13, 2013 13:30:00')
    var date = parse('noon', afternoon);
    assert('12:00:00' == t(date));
    assert('5/14/13' == d(date));
  });
});

/**
 * Weeks
 */

describe('weeks', function() {
  it('next week tuesday', function() {
    var date = parse('next week tuesday', mon);
    assert('1:30:00' == t(date));
    assert('5/21/13' == d(date));
  });

  it('next w tuesday', function() {
    var date = parse('next w tuesday', mon);
    assert('1:30:00' == t(date));
    assert('5/21/13' == d(date));
  });

  it('next wk tuesday', function() {
    var date = parse('next wk tuesday', mon);
    assert('1:30:00' == t(date));
    assert('5/21/13' == d(date));
  });

  it('next week tuesday at 4:30pm', function() {
    var date = parse('next week tuesday at 4:30pm', mon);
    assert('16:30:00' == t(date));
    assert('5/21/13' == d(date));
  });

  it('2 weeks from wednesday', function() {
    var date = parse('2 weeks from wednesday', mon);
    assert('1:30:00' == t(date));
    assert('5/29/13' == d(date));
  });
});

/**
 * Night
 */

describe('night', function() {
  it('night', function() {
    var date = parse('night', mon);
    assert('19:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tomorrow night', function() {
    var date = parse('tomorrow night', mon);
    assert('19:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('tomorrow night at 9', function() {
    var date = parse('tomorrow night at 9', mon);
    assert('21:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('last night', function() {
    var date = parse('last night', mon);
    assert('19:00:00' == t(date));
    assert('5/12/13' == d(date));
  });
})

/**
 * Evening
 */

describe('evening', function() {
  it('evening', function() {
    var date = parse('evening', mon);
    assert('17:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tomorrow evening', function() {
    var date = parse('tomorrow evening', mon);
    assert('17:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('tomorrow evening at 9', function() {
    var date = parse('tomorrow evening at 9', mon);
    assert('21:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('last evening', function() {
    var date = parse('last evening', mon);
    assert('17:00:00' == t(date));
    assert('5/12/13' == d(date));
  });
})

/**
 * Afternoon
 */

describe('afternoon', function() {
  it('afternoon', function() {
    var date = parse('afternoon', mon);
    assert('14:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tomorrow afternoon', function() {
    var date = parse('tomorrow afternoon', mon);
    assert('14:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('last afternoon', function() {
    var date = parse('last afternoon', mon);
    assert('14:00:00' == t(date));
    assert('5/12/13' == d(date));
  });
})

/**
 * Morning
 */

describe('morning', function() {
  it('morning', function() {
    var date = parse('morning', mon);
    assert('8:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('tomorrow morning', function() {
    var date = parse('tomorrow morning', mon);
    assert('8:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('last morning', function() {
    var date = parse('last morning', mon);
    assert('8:00:00' == t(date));
    assert('5/12/13' == d(date));
  });

  it('this morning at 9', function() {
    var date = parse('this morning at 9', mon);
    assert('9:00:00' == t(date));
    assert('5/13/13' == d(date));
  });
})

/**
 * Months
 */

describe('months', function() {
  it('this month', function() {
    var date = parse('this month', mon);
    assert('1:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('next month', function() {
    var date = parse('next month', mon);
    assert('1:30:00' == t(date));
    assert('6/13/13' == d(date));
  });

  it('last month', function() {
    var date = parse('last month', mon);
    assert('1:30:00' == t(date));
    assert('4/13/13' == d(date));
  });

  it('2 months from tomorrow', function() {
    var date = parse('2 months from tomorrow', mon);
    assert('1:30:00' == t(date));
    assert('7/14/13' == d(date));
  });

  it('2M from tomorrow', function() {
    var date = parse('2M from tomorrow', mon);
    assert('1:30:00' == t(date));
    assert('7/14/13' == d(date));
  });

  it('2 monthes from tomorrow (misspelling)', function() {
    var date = parse('2 monthes from tomorrow', mon);
    assert('1:30:00' == t(date));
    assert('7/14/13' == d(date));
  });

  it('should handle months with less days', function() {
    var date = parse('1 month', new Date('01/31/2011'));
    assert('2/28/11' == d(date))
  });

  it('should handle leap year', function() {
    var date = parse('1 month', new Date('01/31/2012'));
    assert('2/29/12' == d(date));
  });

  it('tomorrow afternoon at 4:30pm 1 month from now', function() {
    var date = parse('tomorrow afternoon at 4:30pm 1 month from now', mon);
    assert('16:30:00' == t(date));
    assert('6/14/13' == d(date));
  });
});

/**
 * Year
 */

describe('year', function() {
  it('this year', function() {
    var date = parse('year', mon);
    assert('1:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('this yr', function() {
    var date = parse('year', mon);
    assert('1:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('next year', function() {
    var date = parse('next year', mon);
    assert('1:30:00' == t(date));
    assert('5/13/14' == d(date));
  });

  it('last year', function() {
    var date = parse('last year', mon);
    assert('1:30:00' == t(date));
    assert('5/13/12' == d(date));
  });

  it('2 years from yesterday at 5pm', function() {
    var date = parse('2 years from yesterday at 5pm', mon);
    assert('17:00:00' == t(date));
    assert('5/12/15' == d(date));
  });

  it('2 years ago', function() {
    var date = parse('2 years ago', mon);
    assert('1:30:00' == t(date));
    assert('5/13/11' == d(date));
  })

  it('2 years ago--.', function() {
    var date = parse('2 years ago--.', mon);
    assert('1:30:00' == t(date));
    assert('5/13/11' == d(date));
  })

  it('2 years ago tomorrow', function() {
    var date = parse('2 years ago tomorrow', mon);
    assert('1:30:00' == t(date));
    assert('5/14/11' == d(date));
  })
});

/**
 * Dates in the past
 */

describe('dates in the past', function() {
  var past = new Date('May 13, 2013 18:00:00')

  it('tomorrow afternoon', function() {
    var date = parse('tomorrow afternoon', past);
    assert('14:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('tomorrow afternoon at 3pm', function() {
    var date = parse('tomorrow afternoon at 3pm', past);
    assert('15:00:00' == t(date));
    assert('5/14/13' == d(date));
  });

  // Need to place .nextTime() at the end

  it('3pm tomorrow afternoon', function() {
    var date = parse('3pm tomorrow afternoon', past);
    assert('15:00:00' == t(date));
    assert('5/14/13' == d(date));
  });
});

/**
 * Times
 */
describe('times', function() {
  it('1:30', function() {
    var date = parse('1:30', mon);
    assert('1:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('2:31', function() {
    var date = parse('2:31', mon);
    assert('2:31:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('00:28', function() {
    // past time will result in tomorrow
    var date = parse('00:28', mon);
    assert('0:28:00' == t(date));
    assert('5/14/13' == d(date));
  });
});

/**
 * Ignore other input
 */

describe('other inputs', function() {
  // it('yesterday, 2 years ago--.', function() {
  //   var date = parse('yesterday, 2 years ago--.', mon);
  //   assert('1:30:00' == t(date));
  //   assert('5/12/11' == d(date))
  // });

  it('invalid', function() {
    var date = parse('invalid', mon);
    assert(d(mon) == d(date));
  });

  it('empty', function() {
    var date = parse('', mon);
    assert(d(mon) == d(date));
  });
});

/**
 * Bug fixes
 */

describe('bug fixes', function() {
  it('at 12:30pm (fixes: #6)', function() {
    var after = new Date('May 13, 2013 13:30:00');
    var date = parse('at 12:30pm', after);
    assert('12:30:00' == t(date));
    assert('5/14/13' == d(date));
  });

  it('at X in the morning (fixes: #36)', function() {
    var past = new Date('May 13, 2013 18:00:00')
    var date = parse('tomorrow at 9 in the morning', past);
    assert('9:00:00' == t(date));
    assert('5/14/13' == d(date));
  })
});

/**
 * If context is a string parse it as date
 */

describe('parse context if its a string (fixes: #38)', function() {
  it('string context', function() {
    var today = new Date();
    today.setDate(today.getDate() - 1);
    var date = parse('today at 11am', 'yesterday at 12:30am');

    assert(d(date) == d(today));
    assert('11:00:00' == t(date));
  });
});


/**
 * Support for dates with months
 */

describe('months (fixes: #10)', function() {
  var after = new Date('May 13, 2013 13:30:00');
  it('2nd of January', function() {
    var date = parse('2nd of January 12:30', after);
    assert('12:30:00' == t(date));
    assert('1/2/13' == d(date));
  });

  it('1st of March', function() {
    var date = parse('1st of March', after);
    assert('13:30:00' == t(date));
    assert('3/1/13' == d(date));
  });

  it('1 st of March', function() {
    var date = parse('1 st of March', after);
    assert('13:30:00' == t(date));
    assert('3/1/13' == d(date));
  });

  it('31st of September 4:00am', function() {
    var date = parse('31st of September 4:00am', after);
    assert('4:00:00' == t(date));
    assert('9/31/13' != d(date));
    assert('10/1/13' == d(date));
  });

  it('1st of January 4:00am', function() {
    var date = parse('1st of January 4:00am', after);
    assert('4:00:00' == t(date));
    assert('1/1/13' == d(date));
  })

  it('9th of December 4:00am', function() {
    var date = parse('9th of December 4:00am', after);
    assert('4:00:00' == t(date));
    assert('12/9/13' == d(date));
  })
});

/**
 * Suppport 'ago' modifier
 */

describe('support "ago" modifier (fixes: #20)', function() {
  var after = new Date('May 13, 2013 13:30:00');

  it('x seconds ago', function() {
    var date = parse('10 seconds ago', after);
    assert('13:29:50' == t(date));
    assert('5/13/13' == d(date));
  });

  it('x minutes ago', function() {
    var date = parse('5 minutes ago', after);
    assert('13:25:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('x minute ago', function() {
    var date = parse('1 minutes ago', after);
    assert('13:29:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('x hours ago', function() {
    var date = parse('5 hours ago', after);
    assert('8:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  // !pending operator canonicalization
  // it('x hours from now', function() {
  //   var date = parse('5 hours from now', after);
  //   assert('8:30:00' == t(date));
  //   assert('5/13/13' == d(date));
  // });

  it('x days ago', function() {
    var date = parse('5 day ago', after);
    assert('13:30:00' == t(date));
    assert('5/8/13' == d(date));
  });

  it('x week ago', function() {
    var date = parse('2 week ago', after);
    assert('13:30:00' == t(date));
    assert('4/29/13' == d(date));
  });

  it('x months ago', function() {
    var date = parse('10 months ago', after);
    assert('13:30:00' == t(date));
    assert('7/13/12' == d(date));
  });

  it('x year ago', function() {
    var date = parse('10 year ago', after);
    assert('13:30:00' == t(date));
    assert('5/13/03' == d(date));
  });

});


/**
 * Suppport natural language
 */

describe('support natural language, single-tokens without arithmetics (fixes: #66, 64, 28, 16, 15, 11, 4)', function() {

  it('#66: an hour later', function() {
    var date = parse('an hour later', mon);
    assert('2:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('#64: 2w from wednesday', function() {
    var date = parse('2w from wednesday', mon);
    assert('1:30:00' == t(date));
    assert('5/29/13' == d(date));
  });

  var after = new Date('May 13, 2013 13:30:00');
  it('#28: 2nd day of January', function() {
    var date = parse('2nd day of January 12:30', after);
    assert('12:30:00' == t(date));
    assert('1/2/13' == d(date));
  });

  it('#16: two hours later', function() {
    var date = parse('two hour later', mon);
    assert('3:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('#15: a fortnight from wednesday', function() {
    var date = parse('a fortnight from wednesday', mon);
    assert('1:30:00' == t(date));
    assert('5/29/13' == d(date));
  });

  it('#11: a minute ago', function() {
    var date = parse('a minute ago', after);
    assert('13:29:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('#4: two hours later', function() {
    var date = parse('two hours later', mon);
    assert('3:30:00' == t(date));
    assert('5/13/13' == d(date));
  });

});


/**
 * Suppport arithmetics
 */

describe('support timeline arithmetics (fixes: #70, 62, 21)', function() {

  // !isn't an elegant fix, utilizes util.removeTnPlus for defaulting
  it('#70: 5 days and 2 hours', function() {
    var date = parse('5 days and 2 hours', mon);
    assert('3:30:00' == t(date));
    assert('5/18/13' == d(date));
  });

  it('#62: 5 days 2 hours', function() {
    var date = parse('5 days 2 hours', mon);
    assert('3:30:00' == t(date));
    assert('5/18/13' == d(date));
  });

  it('#62: 2 hours 30 mins', function() {
    var date = parse('2 hours 30 mins', mon);
    assert('4:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  // !pending, need datejs to carry decimals
  // it('#66: half an hour later', function () {
  //   var date = parse('half an hour later', mon);
  //   assert('2:00:00' == t(date));
  //   assert('5/13/13' == d(date));
  // });
  // it('compatible arithmetics: an hour and half later', function () {
  //   var date = parse('an hour and half later', mon);
  //   assert('3:00:00' == t(date));
  //   assert('5/13/13' == d(date));
  // });

  // it('#63: this year', function () {
  //   var date = parse('this year', mon);
  // });

  // it('#63: this week', function () {
  //   var date = parse('this week', mon);
  // });

  // !isn't an elegant fix, utilizes util.removeTnPlus for defaulting
  it('#21: 15 2nd January 12:30', function() {
    var date = parse('on 2nd January 12:30', mon);
    assert('12:30:00' == t(date));
    assert('1/2/13' == d(date));
  });

  // !pending, waiting full CFG arithmetics
  // it('#18: quarter to 12', function() {
  //   var date = parse('quarter to 12', mon);
  //   assert('11:40:00' == t(date));
  //   assert('1/2/13' == d(date));
  // });

  // !pending, waiting full CFG arithmetics
  // it('#25: day after tomorrow', function() {
  //   var date = parse('day after tomorrow', mon);
  //   assert('1:30:00' == t(date));
  //   assert('5/15/13' == d(date));
  // });

});


/**
 * Suppport arithmetics
 */

describe('Time extraction from arbitrary sentence', function() {

  it('time extraction: remind me about laundry in 2 hours 30 mins please', function() {
    var date = parse('remind me about laundry in 2 hours 30 mins please', mon);
    assert('4:00:00' == t(date));
    assert('5/13/13' == d(date));
  });

  it('Normal form: May 13, 2011 01:30:00', function() {
    var date = parse('May 13, 2011 01:30:00', mon);
    assert('1:30:00' == t(date));
    assert('5/13/11' == d(date));
  });

  it('Normal form: 13 May 2011 01:30 UTC', function() {
    // not testing the UTC cuz result will differ across timezones
    var date = parse('13 May 2011 01:30', mon);
    assert('1:30:00' == t(date));
    assert('5/13/11' == d(date));
  });

})


/**
 * Time helper function
 */

function t(date) {
  var t = date.toTimeString().split(' ')[0];
  t = ('0' == t[0]) ? t.slice(1) : t;
  return t;
}

/**
 * Date helper function
 */

function d(date) {
  var d = date.toString();
  var month = date.getMonth() + 1;
  var day = date.getDate();
  var year = '' + date.getFullYear();
  return [month, day, year.slice(2)].join('/');
}
