import responses
import unittest
import time

from tests.support import with_resource, with_fixture, characters

from twitter_ads.account import Account
from twitter_ads.campaign import Campaign
from twitter_ads.client import Client
from twitter_ads.cursor import Cursor
from twitter_ads.http import Request
from twitter_ads.resource import Resource
from twitter_ads import API_VERSION
from twitter_ads.error import RateLimit


@responses.activate
def test_rate_limit_handle_with_retry_success_1(monkeypatch):
    # scenario:
    #  - 500 (retry) -> 429 (handle rate limit) -> 200 (end)
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=500,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=429,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': str(int(time.time()) + 5)
                  })

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=200,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '9999',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40),
        options={
            'handle_rate_limit': True,
            'retry_max': 1,
            'retry_delay': 3000,
            'retry_on_status': [500]
        }
    )

    account = Account.load(client, '2iqph')

    cursor = Campaign.all(account)
    assert len(responses.calls) == 4
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.rate_limit is None
    assert cursor.account_rate_limit == '10000'
    assert cursor.account_rate_limit_remaining == '9999'
    assert cursor.account_rate_limit_reset == '1546300800'


@responses.activate
def test_rate_limit_handle_with_retry_success_2(monkeypatch):
    # scenario:
    #  - 429 (handle rate limit) -> 500 (retry) -> 200 (end)
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=429,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=500,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': str(int(time.time()) + 5)
                  })

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=200,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '9999',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40),
        options={
            'handle_rate_limit': True,
            'retry_max': 1,
            'retry_delay': 3000,
            'retry_on_status': [500]
        }
    )

    account = Account.load(client, '2iqph')

    cursor = Campaign.all(account)
    assert len(responses.calls) == 4
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.rate_limit is None
    assert cursor.account_rate_limit == '10000'
    assert cursor.account_rate_limit_remaining == '9999'
    assert cursor.account_rate_limit_reset == '1546300800'


@responses.activate
def test_rate_limit_handle_success(monkeypatch):
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=429,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': str(int(time.time()) + 5)
                  })

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=200,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '9999',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40),
        options={
            'handle_rate_limit': True
        }
    )

    account = Account.load(client, '2iqph')

    cursor = Campaign.all(account)
    assert len(responses.calls) == 3
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.rate_limit is None
    assert cursor.account_rate_limit == '10000'
    assert cursor.account_rate_limit_remaining == '9999'
    assert cursor.account_rate_limit_reset == '1546300800'


@responses.activate
def test_rate_limit_handle_error(monkeypatch):
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=429,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': str(int(time.time()) + 5)
                  })

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  status=429,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40),
        options={
            'handle_rate_limit': True
        }
    )

    account = Account.load(client, '2iqph')

    try:
        cursor = Campaign.all(account)
    except Exception as e:
        error = e
        print(error)
    assert len(responses.calls) == 3
    assert isinstance(error, RateLimit)
    assert error.reset_at == '1546300800'


@responses.activate
def test_rate_limit_cursor_class_access():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns'),
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '9999',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')

    cursor = Campaign.all(account)
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.rate_limit is None
    assert cursor.account_rate_limit == '10000'
    assert cursor.account_rate_limit_remaining == '9999'
    assert cursor.account_rate_limit_reset == '1546300800'


@responses.activate
def test_rate_limit_resource_class_access():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/campaigns/2wap7'),
                  body=with_fixture('campaigns_load'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '9999',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')
    campaign = Campaign.load(account, '2wap7')

    resource = '/' + API_VERSION + '/accounts/2iqph/campaigns/2wap7'
    params = {}

    response = Request(client, 'get', resource, params=params).perform()
    # from_response() is a staticmethod, so passing campaign instance as dummy.
    # We can later change this test case to not call this manually
    # once we changed existing classes to pass the header argument.
    data = campaign.from_response(response.body['data'], response.headers)

    assert data is not None
    assert isinstance(data, Resource)
    assert data.id == '2wap7'
    assert data.entity_status == 'ACTIVE'
    assert data.rate_limit is None
    assert data.account_rate_limit == '10000'
    assert data.account_rate_limit_remaining == '9999'
    assert data.account_rate_limit_reset == '1546300800'
