# coding: utf-8
import logging
import pickle

from datetime import datetime, timedelta

import pytest

from sentry_sdk import Hub, Client, add_breadcrumb, capture_message
from sentry_sdk.transport import _parse_rate_limits
from sentry_sdk.integrations.logging import LoggingIntegration


@pytest.fixture
def make_client(request, httpserver):
    def inner(**kwargs):
        return Client(
            "http://foobar@{}/132".format(httpserver.url[len("http://") :]), **kwargs
        )

    return inner


@pytest.mark.forked
@pytest.mark.parametrize("debug", (True, False))
@pytest.mark.parametrize("client_flush_method", ["close", "flush"])
@pytest.mark.parametrize("use_pickle", (True, False))
def test_transport_works(
    httpserver,
    request,
    capsys,
    caplog,
    debug,
    make_client,
    client_flush_method,
    use_pickle,
    maybe_monkeypatched_threading,
):
    httpserver.serve_content("ok", 200)
    caplog.set_level(logging.DEBUG)
    client = make_client(debug=debug)

    if use_pickle:
        client = pickle.loads(pickle.dumps(client))

    Hub.current.bind_client(client)
    request.addfinalizer(lambda: Hub.current.bind_client(None))

    add_breadcrumb(level="info", message="i like bread", timestamp=datetime.utcnow())
    capture_message("löl")

    getattr(client, client_flush_method)()

    out, err = capsys.readouterr()
    assert not err and not out
    assert httpserver.requests

    assert any("Sending event" in record.msg for record in caplog.records) == debug


def test_transport_infinite_loop(httpserver, request, make_client):
    httpserver.serve_content("ok", 200)

    client = make_client(
        debug=True,
        # Make sure we cannot create events from our own logging
        integrations=[LoggingIntegration(event_level=logging.DEBUG)],
    )

    with Hub(client):
        capture_message("hi")
        client.flush()

    assert len(httpserver.requests) == 1


NOW = datetime(2014, 6, 2)


@pytest.mark.parametrize(
    "input,expected",
    [
        # Invalid rate limits
        ("", {}),
        ("invalid", {}),
        (",,,", {}),
        (
            "42::organization, invalid, 4711:foobar;transaction;security:project",
            {
                None: NOW + timedelta(seconds=42),
                "transaction": NOW + timedelta(seconds=4711),
                "security": NOW + timedelta(seconds=4711),
                # Unknown data categories
                "foobar": NOW + timedelta(seconds=4711),
            },
        ),
        (
            "4711:foobar;;transaction:organization",
            {
                "transaction": NOW + timedelta(seconds=4711),
                # Unknown data categories
                "foobar": NOW + timedelta(seconds=4711),
                "": NOW + timedelta(seconds=4711),
            },
        ),
    ],
)
def test_parse_rate_limits(input, expected):
    assert dict(_parse_rate_limits(input, now=NOW)) == expected


def test_simple_rate_limits(httpserver, capsys, caplog, make_client):
    client = make_client()
    httpserver.serve_content("no", 429, headers={"Retry-After": "4"})

    client.capture_event({"type": "transaction"})
    client.flush()

    assert len(httpserver.requests) == 1
    assert httpserver.requests[0].url.endswith("/api/132/envelope/")
    del httpserver.requests[:]

    assert set(client.transport._disabled_until) == set([None])

    client.capture_event({"type": "transaction"})
    client.capture_event({"type": "event"})
    client.flush()

    assert not httpserver.requests


@pytest.mark.parametrize("response_code", [200, 429])
def test_data_category_limits(httpserver, capsys, caplog, response_code, make_client):
    client = make_client()
    httpserver.serve_content(
        "hm",
        response_code,
        headers={"X-Sentry-Rate-Limits": "4711:transaction:organization"},
    )

    client.capture_event({"type": "transaction"})
    client.flush()

    assert len(httpserver.requests) == 1
    assert httpserver.requests[0].url.endswith("/api/132/envelope/")
    del httpserver.requests[:]

    assert set(client.transport._disabled_until) == set(["transaction"])

    client.capture_event({"type": "transaction"})
    client.capture_event({"type": "transaction"})
    client.flush()

    assert not httpserver.requests

    client.capture_event({"type": "event"})
    client.flush()

    assert len(httpserver.requests) == 1


@pytest.mark.parametrize("response_code", [200, 429])
def test_complex_limits_without_data_category(
    httpserver, capsys, caplog, response_code, make_client
):
    client = make_client()
    httpserver.serve_content(
        "hm",
        response_code,
        headers={"X-Sentry-Rate-Limits": "4711::organization"},
    )

    client.capture_event({"type": "transaction"})
    client.flush()

    assert len(httpserver.requests) == 1
    assert httpserver.requests[0].url.endswith("/api/132/envelope/")
    del httpserver.requests[:]

    assert set(client.transport._disabled_until) == set([None])

    client.capture_event({"type": "transaction"})
    client.capture_event({"type": "transaction"})
    client.capture_event({"type": "event"})
    client.flush()

    assert len(httpserver.requests) == 0
