from datetime import datetime, timedelta

from importlib import import_module
openpaygo_token = import_module("openpaygo-token")
simulators = import_module("openpaygo-token.simulators")


def assert_time_equals(time1, time2):
    assert time1.replace(second=0, microsecond=0) == time2.replace(second=0, microsecond=0)


if __name__ == '__main__':
    # ------ IMPORTANT WARNING --------
    # DO NOT USE THIS KEY IN PRODUCTION
    # IT IS JUST AN EXAMPLE
    # ---------------------------------
    device_key = b'\xa2\x9a\xb8.\xdc_\xbb\xc4\x1e\xc9S\x0fm\xac\x86\xb1'
    device_starting_code = 123456789 # Generated by fair dice roll
    restricted_digit_set = False

    print('Device: We initiate the device simulator with our device')
    device_simulator = simulators.DeviceSimulator(device_starting_code, device_key,
                                       restricted_digit_set=restricted_digit_set,
                                       waiting_period_enabled=False)
    print('Server: We initiate the server simulator with our device')
    server_simulator = simulators.SingleDeviceServerSimulator(device_starting_code, device_key,
                                                   restricted_digit_set=restricted_digit_set)

    print('\n')
    print('Device: We try entering an invalid token into the device: 123456789')
    device_simulator.enter_token('123456789')
    print('Device: We check the device status (should be still inactive)')
    device_simulator.print_status()

    print('\n')
    print('Server: We add 1 days of activation for the device')
    this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1))
    print('Token: '+this_token)
    print('Device: We enter the generated token into the device')
    device_simulator.enter_token(this_token)
    print('Device: We check the device status (should be active with 1 day)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1))

    print('\n')
    print('Device: We enter the token a second time to make sure it doesnt add the days again')
    device_simulator.enter_token(this_token)
    print('Device: We check the device status (should be active with 1 day)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1))

    print('\n')
    print('Server: We set it to expire in 30 days')
    this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=30))
    print('Token: ' + this_token)
    print('Device: We enter the generated token into the device')
    device_simulator.enter_token(this_token)
    print('Device: We check the device status (should be active with 30 days)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=30))

    print('\n')
    print('Server: We set it to expire in 7 days (removing 23 days)')
    this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=7))
    print('Token: ' + this_token)
    print('Device: We enter the generated token into the device')
    device_simulator.enter_token(this_token)
    print('Device: We check the device status (should be active with 7 days)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=7))

    print('\n')
    print('Server: We generate a token for putting the device in PAYG-OFF mode')
    this_PAYG_OFF_code = server_simulator.generate_payg_disable_token()
    this_PAYG_OFF_code = str(this_PAYG_OFF_code)
    print('Token: ' + this_PAYG_OFF_code)
    print('Device: We enter the generated token into the device')
    device_simulator.enter_token(this_PAYG_OFF_code)
    print('Device: We check the device status (should be active forver)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert device_simulator.payg_enabled is False

    print('\n')
    print('Server: We generate a token for putting the device back PAYG-ON mode with 0 days')
    this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=0))
    this_token = str(this_token)
    print('Token: ' + this_token)
    print('Device: We enter the generated token into the device')
    device_simulator.enter_token(this_token)
    print('Device: We check the device status (should not be active)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert device_simulator.payg_enabled is True
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0))

    print('\n')
    print('Server: We generate a bunch of 1 day tokens, but only enter the latest one')
    server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1))
    server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1))
    server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1))
    server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1))
    server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1))
    this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1))
    this_token = str(this_token)
    print('Token: ' + this_token)
    print('Device: We enter the latest generated token into the device')
    device_simulator.enter_token(this_token)
    print('Device: We check the device status (should be active with 1 day and the count synchronised with the server)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1))

    print('\n')
    print('Server: We add generate 9 tokens each add-time of 1 day')
    token_1 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_2 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_3 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_4 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_5 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_6 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_7 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_8 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_9 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    print('Tokens: ', token_1, token_2, token_3, token_4, token_5, token_6, token_7, token_8, token_9)
    print('Device: We enter the 9th token into the device')
    device_simulator.enter_token(token_9)
    print('Device: We check the device status (should be active with +1 day (2 days total)')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=2))
    print('Device: We enter the 1st token into the device')
    device_simulator.enter_token(token_1)
    print('Device: We check the device status , it should not have changed, because its more than 5 add times before')
    device_simulator.print_status()
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=2))
    print('Device: We enter the tokens 5, 4, 3 and 2 into the device')
    device_simulator.enter_token(token_5)
    device_simulator.enter_token(token_4)
    device_simulator.enter_token(token_3)
    device_simulator.enter_token(token_2)
    print('Device: We check the device status , it should have +4 days (6 days total)')
    device_simulator.print_status()
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=6))

    print('\n')
    print('Server: We add generate 2 tokens, first add-time and then set-time')
    token_1 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    token_2 = server_simulator._generate_token_from_value(0, openpaygo_token.OPAYGOShared.TOKEN_TYPE_SET_TIME)
    print('Tokens: ', token_1, token_2)
    print('Device: We enter the 2nd token')
    device_simulator.enter_token(token_2)
    print('Device: We check the device status should be active in 0 days.')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0))
    print('Device: We enter the 1st token into the device')
    device_simulator.enter_token(token_1)
    print('Device: We check the device status , it should not have changed, '
          'because you cannot use an add-time token older than a set-time')
    device_simulator.print_status()
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0))

    print('\n')
    print('Server: We add generate 2 tokens, first add-time and then set-time')
    token_1 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_SET_TIME)
    token_2 = server_simulator._generate_token_from_value(1, openpaygo_token.OPAYGOShared.TOKEN_TYPE_ADD_TIME)
    print('Tokens: ', token_1, token_2)
    print('Device: We enter the 2nd token')
    device_simulator.enter_token(token_2)
    print('Device: We check the device status should be active in 0 days.')
    device_simulator.print_status()
    assert device_simulator.count == server_simulator.count
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1))
    print('Device: We enter the 1st token into the device')
    device_simulator.enter_token(token_1)
    print('Device: We check the device status , it should not have changed, '
          'because you cannot use an older set-time token')
    device_simulator.print_status()
    assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1))
