import pytest
from pydantic import ValidationError

from delta_barth.api import common
from delta_barth.constants import DEFAULT_API_ERR_CODE, HTTP_BASE_CONTENT_HEADERS
from delta_barth.errors import (
    UnspecifiedRequestType,
)
from delta_barth.types import HttpRequestTypes


@pytest.mark.parametrize(
    ["case", "expect"],
    [
        ("http://test.com/ ", "http://test.com"),
        ("http://test.com/", "http://test.com"),
        ("http://test.com ", "http://test.com"),
        ("http://test.com// ", "http://test.com"),
        ("http://test.com", "http://test.com"),
        (" /http://test.com", "http://test.com"),
        ("  //http://test.com", "http://test.com"),
        ("//http://test.com", "http://test.com"),
    ],
)
def test_strip_url_components(case, expect):
    res = common._strip_url_components(case)
    assert res == expect


@pytest.mark.parametrize(
    ["base", "route", "expect"],
    [
        ("http://test.com/ ", "ping", "http://test.com/ping"),
        ("http://test.com/", "ping  ", "http://test.com/ping"),
        ("http://test.com ", "ping/", "http://test.com/ping"),
        ("http://test.com// ", "ping", "http://test.com/ping"),
        ("http://test.com", "/ping  ", "http://test.com/ping"),
        (" /http://test.com", "/ ping/ ", "http://test.com/ping"),
        ("  //http://test.com", "ping", "http://test.com/ping"),
        ("//http://test.com", "//    ping// ", "http://test.com/ping"),
    ],
)
def test_combine_route(base, route, expect):
    res = common.combine_route(base, route)
    assert res == expect


def test_validate_creds(credentials):
    creds = common.validate_credentials(
        user_name=credentials["user"],
        password=credentials["pwd"],
        database=credentials["db"],
        mandant=credentials["mandant"],
    )
    assert creds.user_name == credentials["user"]
    assert creds.password == credentials["pwd"]
    assert creds.database == credentials["db"]
    assert creds.mandant == credentials["mandant"]
    # with whitespaces
    user = "  " + credentials["user"] + " "
    creds = common.validate_credentials(
        user_name=user,
        password=credentials["pwd"],
        database=credentials["db"],
        mandant=credentials["mandant"],
    )
    assert user != credentials["user"]
    assert creds.user_name == credentials["user"]
    # invalid type
    user = 123
    with pytest.raises(ValidationError):
        creds = common.validate_credentials(
            user_name=user,  # type: ignore
            password=credentials["pwd"],
            database=credentials["db"],
            mandant=credentials["mandant"],
        )


# TODO removal
# def test_assert_login():
#     with pytest.raises(ApiConnectionError):
#         common._assert_login_status()


@pytest.mark.api_con_required
def test_ping(api_base_url):
    resp = common.ping(api_base_url, HttpRequestTypes.GET)
    assert resp.status_code == 204
    resp = common.ping(api_base_url, HttpRequestTypes.PUT)
    assert resp.status_code == 204
    resp = common.ping(api_base_url, HttpRequestTypes.DELETE)
    assert resp.status_code == 204

    with pytest.raises(UnspecifiedRequestType):
        resp = common.ping(api_base_url, HttpRequestTypes.POST)


def test_session(credentials, api_base_url):
    session = common.Session(HTTP_BASE_CONTENT_HEADERS)

    assert session.session_token is None
    assert session._creds is None
    assert session._base_url is None

    session.set_base_url(api_base_url)
    assert session._base_url is not None
    session.set_credentials(
        user_name=credentials["user"],
        password=credentials["pwd"],
        database=credentials["db"],
        mandant=credentials["mandant"],
    )
    assert session._creds is not None

    assert session.session_token is None
    assert not session.logged_in


@pytest.mark.api_con_required
def test_login_logout(session, credentials):
    assert not session.logged_in

    resp, status = session.login()
    assert resp is not None
    assert status.code == 0
    assert session.session_token is not None
    resp, status = session.logout()
    assert resp is None
    assert status.code == 0
    assert session.session_token is None
    assert "DelecoToken" not in session.headers

    session.set_credentials(
        user_name=credentials["user"],
        password="WRONG_PASSWORD",
        database=credentials["db"],
        mandant=credentials["mandant"],
    )
    resp, status = session.login()
    assert resp is not None
    assert status.code == DEFAULT_API_ERR_CODE
    assert status.api_server_error is not None
    assert status.api_server_error.status_code == 409
    assert status.api_server_error.message == "Nutzer oder Passwort falsch."


@pytest.mark.api_con_required
def test_assert_login_while_logged_out(session):
    assert session.session_token is None
    assert session._creds is not None
    # test logged out state
    resp, status = session.assert_login()
    assert resp is not None
    assert status.code == 0
    assert session.session_token is not None
    resp, status = session.logout()
    assert status.code == 0

    # test already logged in
    assert session.session_token is None
    assert session._creds is not None
    _, status = session.login()
    assert status.code == 0
    resp, status = session.assert_login()
    assert resp is not None
    assert status.code == 0
    assert session.session_token is not None
    resp, status = session.logout()
    assert status.code == 0

    # test invalid token
    assert session.session_token is None
    assert session._creds is not None
    _, status = session.login()
    assert status.code == 0
    session._session_token = "WRONGTOKEN"
    resp, status = session.assert_login()
    assert resp is not None
    assert status.code == 0
    assert session.session_token is not None
    resp, status = session.logout()
    assert status.code == 0
