import os
import logging

import pytest
# from flask import Flask

from dash import Dash, exceptions as _exc

# # noinspection PyProtectedMember
# from dash._configs import (
#     pathname_configs,
#     DASH_ENV_VARS,
#     get_combined_config,
#     load_dash_env_vars,
# )
from dash._utils import get_asset_path, get_relative_path, strip_relative_path


# @pytest.fixture
# def empty_environ():
#     for k in DASH_ENV_VARS.keys():
#         if k in os.environ:
#             os.environ.pop(k)


# def test_dash_env_vars(empty_environ):
#     assert {None} == {
#         val for _, val in DASH_ENV_VARS.items()
#     }, "initial var values are None without extra OS environ setting"


@pytest.mark.skip
@pytest.mark.parametrize(
    "route_prefix, req_prefix, expected_route, expected_req",
    [
        (None, None, "/", "/"),
        ("/dash/", None, None, "/dash/"),
        (None, "/my-dash-app/", "/", "/my-dash-app/"),
        ("/dash/", "/my-dash-app/dash/", "/dash/", "/my-dash-app/dash/"),
    ],
)
def test_valid_pathname_prefix_init(
    empty_environ, route_prefix, req_prefix, expected_route, expected_req
):
    _, routes, req = pathname_configs(
        routes_pathname_prefix=route_prefix, requests_pathname_prefix=req_prefix
    )

    if expected_route is not None:
        assert routes == expected_route
    assert req == expected_req


@pytest.mark.skip
def test_invalid_pathname_prefix(empty_environ):
    with pytest.raises(_exc.InvalidConfig, match="url_base_pathname"):
        _, _, _ = pathname_configs("/my-path", "/another-path")

    with pytest.raises(_exc.InvalidConfig) as excinfo:
        _, _, _ = pathname_configs(
            url_base_pathname="/invalid", routes_pathname_prefix="/invalid"
        )
    assert str(excinfo.value).split(".")[0].endswith("`routes_pathname_prefix`")

    with pytest.raises(_exc.InvalidConfig) as excinfo:
        _, _, _ = pathname_configs(
            url_base_pathname="/my-path", requests_pathname_prefix="/another-path"
        )
    assert str(excinfo.value).split(".")[0].endswith("`requests_pathname_prefix`")

    with pytest.raises(_exc.InvalidConfig, match="start with `/`"):
        _, _, _ = pathname_configs("my-path")

    with pytest.raises(_exc.InvalidConfig, match="end with `/`"):
        _, _, _ = pathname_configs("/my-path")


@pytest.mark.skip
def test_pathname_prefix_from_environ_app_name(empty_environ):
    os.environ["DASH_APP_NAME"] = "my-dash-app"
    _, routes, req = pathname_configs()
    assert req == "/my-dash-app/"
    assert routes == "/"


@pytest.mark.skip
def test_pathname_prefix_environ_routes(empty_environ):
    os.environ["DASH_ROUTES_PATHNAME_PREFIX"] = "/routes/"
    _, routes, _ = pathname_configs()
    assert routes == "/routes/"


@pytest.mark.skip
def test_pathname_prefix_environ_requests(empty_environ):
    os.environ["DASH_REQUESTS_PATHNAME_PREFIX"] = "/requests/"
    _, _, req = pathname_configs()
    assert req == "/requests/"


# @pytest.mark.parametrize(
#     "req, expected",
#     [
#         ("/", "/assets/reset.css"),
#         ("/requests/", "/requests/assets/reset.css"),
#         ("/requests/routes/", "/requests/routes/assets/reset.css"),
#     ],
# )
# def test_pathname_prefix_assets(empty_environ, req, expected):
#     path = get_asset_path(req, "reset.css", "assets")
#     assert path == expected


@pytest.mark.skip
def test_get_combined_config_dev_tools_ui(empty_environ):
    val1 = get_combined_config("ui", None, default=False)
    assert (
        not val1
    ), "should return the default value if None is provided for init and environment"

    os.environ["DASH_UI"] = "true"
    val2 = get_combined_config("ui", None, default=False)
    assert val2, "should return the set environment value as True"

    val3 = get_combined_config("ui", False, default=True)
    assert not val3, "init value overrides the environment value"


@pytest.mark.skip
def test_get_combined_config_props_check(empty_environ):
    val1 = get_combined_config("props_check", None, default=False)
    assert (
        not val1
    ), "should return the default value if None is provided for init and environment"

    os.environ["DASH_PROPS_CHECK"] = "true"
    val2 = get_combined_config("props_check", None, default=False)
    assert val2, "should return the set environment value as True"

    val3 = get_combined_config("props_check", False, default=True)
    assert not val3, "init value overrides the environment value"


@pytest.mark.skip
def test_load_dash_env_vars_refects_to_os_environ(empty_environ):
    for var in DASH_ENV_VARS.keys():
        os.environ[var] = "true"
        vars = load_dash_env_vars()
        assert vars[var] == "true"

        os.environ[var] = "false"
        vars = load_dash_env_vars()
        assert vars[var] == "false"


# @pytest.mark.parametrize(
#     "name, server, expected",
#     [
#         (None, True, "__main__"),
#         ("test", True, "test"),
#         ("test", False, "test"),
#         (None, Flask("test"), "test"),
#         ("test", Flask("other"), "test"),
#     ],
# )
# def test_app_name_server(empty_environ, name, server, expected):
#     app = Dash(name=name, server=server)
#     assert app.config.name == expected


@pytest.mark.parametrize(
    "prefix, partial_path, expected",
    [
        ("/", "", "/"),
        ("/my-dash-app/", "", "/my-dash-app/"),
        ("/", "/", "/"),
        ("/my-dash-app/", "/", "/my-dash-app/"),
        ("/", "/page-1", "/page-1"),
        ("/my-dash-app/", "/page-1", "/my-dash-app/page-1"),
        ("/", "/page-1/", "/page-1/"),
        ("/my-dash-app/", "/page-1/", "/my-dash-app/page-1/"),
        ("/", "/page-1/sub-page-1", "/page-1/sub-page-1"),
        ("/my-dash-app/", "/page-1/sub-page-1", "/my-dash-app/page-1/sub-page-1"),
    ],
)
def test_pathname_prefix_relative_url(prefix, partial_path, expected):
    path = get_relative_path(prefix, partial_path)
    assert path == expected


@pytest.mark.parametrize(
    "prefix, partial_path",
    [("/", "relative-page-1"), ("/my-dash-app/", "relative-page-1")],
)
def test_invalid_get_relative_path(prefix, partial_path):
    with pytest.raises(_exc.UnsupportedRelativePath):
        get_relative_path(prefix, partial_path)


@pytest.mark.parametrize(
    "prefix, partial_path, expected",
    [
        ("/", None, None),
        ("/my-dash-app/", None, None),
        ("/", "/", ""),
        ("/my-dash-app/", "/my-dash-app", ""),
        ("/my-dash-app/", "/my-dash-app/", ""),
        ("/", "/page-1", "page-1"),
        ("/my-dash-app/", "/my-dash-app/page-1", "page-1"),
        ("/", "/page-1/", "page-1"),
        ("/my-dash-app/", "/my-dash-app/page-1/", "page-1"),
        ("/", "/page-1/sub-page-1", "page-1/sub-page-1"),
        ("/my-dash-app/", "/my-dash-app/page-1/sub-page-1", "page-1/sub-page-1"),
        ("/", "/page-1/sub-page-1/", "page-1/sub-page-1"),
        ("/my-dash-app/", "/my-dash-app/page-1/sub-page-1/", "page-1/sub-page-1"),
        ("/my-dash-app/", "/my-dash-app/my-dash-app/", "my-dash-app"),
        (
            "/my-dash-app/",
            "/my-dash-app/something-else/my-dash-app/",
            "something-else/my-dash-app",
        ),
    ],
)
def test_strip_relative_path(prefix, partial_path, expected):
    path = strip_relative_path(prefix, partial_path)
    assert path == expected


@pytest.mark.parametrize(
    "prefix, partial_path",
    [
        ("/", "relative-page-1"),
        ("/my-dash-app", "relative-page-1"),
        ("/my-dash-app", "/some-other-path"),
    ],
)
def test_invalid_strip_relative_path(prefix, partial_path):
    with pytest.raises(_exc.UnsupportedRelativePath):
        strip_relative_path(prefix, partial_path)


@pytest.mark.skip
def test_port_env_fail_str(empty_environ):
    app = Dash()
    with pytest.raises(Exception) as excinfo:
        app.run_server(port="garbage")
    assert (
        excinfo.exconly()
        == "ValueError: Expecting an integer from 1 to 65535, found port='garbage'"
    )


@pytest.mark.skip
def test_port_env_fail_range(empty_environ):
    app = Dash()
    with pytest.raises(Exception) as excinfo:
        app.run_server(port="0")
    assert (
        excinfo.exconly()
        == "AssertionError: Expecting an integer from 1 to 65535, found port=0"
    )

    with pytest.raises(Exception) as excinfo:
        app.run_server(port="65536")
    assert (
        excinfo.exconly()
        == "AssertionError: Expecting an integer from 1 to 65535, found port=65536"
    )


@pytest.mark.skip
@pytest.mark.parametrize(
    "setlevel_warning", [False, True],
)
def test_no_proxy_success(mocker, caplog, empty_environ, setlevel_warning):
    app = Dash()

    if setlevel_warning:
        app.logger.setLevel(logging.WARNING)

    # mock out the run method so we don't actually start listening forever
    mocker.patch.object(app.server, "run")

    app.run_server(port=8787)

    STARTUP_MESSAGE = "Dash is running on http://127.0.0.1:8787/\n"
    if setlevel_warning:
        assert caplog.text is None or STARTUP_MESSAGE not in caplog.text
    else:
        assert STARTUP_MESSAGE in caplog.text


@pytest.mark.skip
@pytest.mark.parametrize(
    "proxy, host, port, path",
    [
        ("https://daash.plot.ly", "127.0.0.1", 8050, "/"),
        ("https://daaash.plot.ly", "0.0.0.0", 8050, "/a/b/c/"),
        ("https://daaaash.plot.ly", "127.0.0.1", 1234, "/"),
        ("http://go.away", "127.0.0.1", 8050, "/now/"),
        ("http://my.server.tv:8765", "0.0.0.0", 80, "/"),
    ],
)
def test_proxy_success(mocker, caplog, empty_environ, proxy, host, port, path):
    proxystr = "http://{}:{}::{}".format(host, port, proxy)
    app = Dash(url_base_pathname=path)
    mocker.patch.object(app.server, "run")

    app.run_server(proxy=proxystr, host=host, port=port)

    assert "Dash is running on {}{}\n".format(proxy, path) in caplog.text


@pytest.mark.skip
def test_proxy_failure(mocker, empty_environ):
    app = Dash()

    # if the tests work we'll never get to server.run, but keep the mock
    # in case something is amiss and we don't get an exception.
    mocker.patch.object(app.server, "run")

    with pytest.raises(_exc.ProxyError) as excinfo:
        app.run_server(
            proxy="https://127.0.0.1:8055::http://plot.ly", host="127.0.0.1", port=8055
        )
    assert "protocol: http is incompatible with the proxy" in excinfo.exconly()
    assert "you must use protocol: https" in excinfo.exconly()

    with pytest.raises(_exc.ProxyError) as excinfo:
        app.run_server(
            proxy="http://0.0.0.0:8055::http://plot.ly", host="127.0.0.1", port=8055
        )
    assert "host: 127.0.0.1 is incompatible with the proxy" in excinfo.exconly()
    assert "you must use host: 0.0.0.0" in excinfo.exconly()

    with pytest.raises(_exc.ProxyError) as excinfo:
        app.run_server(
            proxy="http://0.0.0.0:8155::http://plot.ly", host="0.0.0.0", port=8055
        )
    assert "port: 8055 is incompatible with the proxy" in excinfo.exconly()
    assert "you must use port: 8155" in excinfo.exconly()


@pytest.mark.skip
def test_title():
    app = Dash()
    assert "<title>Dash</title>" in app.index()
    app = Dash()
    app.title = "Hello World"
    assert "<title>Hello World</title>" in app.index()
    app = Dash(title="Custom Title")
    assert "<title>Custom Title</title>" in app.index()
