import pytest

from flexmeasures.utils.entity_address_utils import (
    build_entity_address,
    parse_entity_address,
    EntityAddressException,
    build_ea_scheme_and_naming_authority,
    reverse_domain_name,
)
from flexmeasures.utils.time_utils import get_first_day_of_next_month


@pytest.mark.parametrize(
    "info, entity_type, host, fm_scheme, exp_result",
    [
        (
            dict(sensor_id=42),
            "sensor",
            "flexmeasures.io",
            "fm1",
            "ea1.2021-01.io.flexmeasures:fm1.42",
        ),
        (
            dict(asset_id=42),
            "sensor",
            "flexmeasures.io",
            "fm1",
            "required field 'sensor_id'",
        ),
        (
            dict(sensor_id=40),
            "connection",
            "flexmeasures.io",
            "fm1",
            "ea1.2021-01.io.flexmeasures:fm1.40",
        ),
        (
            dict(asset_id=40),
            "connection",
            "flexmeasures.io",
            "fm1",
            "required field 'sensor_id'",
        ),
        (
            dict(owner_id=3, asset_id=40),
            "connection",
            "flexmeasures.io",
            "fm0",
            "ea1.2021-01.io.flexmeasures:fm0.3:40",
        ),
        (
            dict(owner_id=3),
            "connection",
            "flexmeasures.io",
            "fm0",
            "required field 'asset_id'",
        ),
        (
            dict(owner_id=40, asset_id=30),
            "connection",
            "localhost:5000",
            "fm0",
            f"ea1.{get_first_day_of_next_month().strftime('%Y-%m')}.localhost:fm0.40:30",
        ),
        (
            dict(
                weather_sensor_type_name="temperature",
                latitude=52,
                longitude=73.0,
            ),
            "weather_sensor",
            "flexmeasures.io",
            "fm0",
            "ea1.2021-01.io.flexmeasures:fm0.temperature:52:73.0",
        ),
        (
            dict(market_name="epex_da"),
            "market",
            "flexmeasures.io",
            "fm0",
            "ea1.2021-01.io.flexmeasures:fm0.epex_da",
        ),
        (
            dict(
                owner_id=40,
                asset_id=30,
                event_type="soc",
                event_id=302,
            ),
            "event",
            "http://staging.flexmeasures.io:4444",
            "fm0",
            "ea1.2022-09.io.flexmeasures.staging:fm0.40:30:302:soc",
        ),
    ],
)
def test_build_entity_address(
    app, info: dict, entity_type: str, host: str, fm_scheme: str, exp_result: str
):
    with app.app_context():
        app.config["FLEXMEASURES_HOSTS_AND_AUTH_START"] = {
            "flexmeasures.io": "2021-01",
            "staging.flexmeasures.io": "2022-09",
        }
        if exp_result.startswith("ea1"):
            assert (
                build_entity_address(info, entity_type, host, fm_scheme) == exp_result
            )
        else:
            with pytest.raises(EntityAddressException, match=exp_result):
                build_entity_address(info, entity_type, host, fm_scheme) == exp_result


@pytest.mark.parametrize(
    "entity_type, entity_address, exp_result",
    [
        (
            "connection",
            "ea2.2018-06.localhost:40:30",
            "starts with 'ea1'",
        ),
        (
            "connection",
            "ea1.2018-RR.localhost:40:30",
            "date specification",
        ),
        (
            "weather_sensor",
            "ea1.2018-04.localhost:5000:40:30",
            "Could not parse",  # no sensor type (which starts with a letter)
        ),
        ("connection", "ea1:40", "a date spec"),  # trying only an asset ID
        (
            "connection",
            "ea1.2018-06.localhost:5000:40:30",
            dict(naming_authority="2018-06.localhost", owner_id=40, asset_id=30),
        ),
        (
            "connection",
            "ea1.2018-06.localhost:40",
            dict(naming_authority="2018-06.localhost", asset_id=40, owner_id=None),
        ),
        (
            "connection",
            "ea1.2018-06.io.flexmeasures:40:30",
            dict(naming_authority="2018-06.io.flexmeasures", owner_id=40, asset_id=30),
        ),
        (
            "weather_sensor",
            "ea1.2018-06.io.flexmeasures:temperature:-52:73.0",
            dict(
                naming_authority="2018-06.io.flexmeasures",
                weather_sensor_type_name="temperature",
                latitude=-52,
                longitude=73.0,
            ),
        ),
        (
            "market",
            "ea1.2018-06.io.flexmeasures:epex_da",
            dict(naming_authority="2018-06.io.flexmeasures", market_name="epex_da"),
        ),
        (
            "event",
            "ea1.2018-06.io.flexmeasures.staging:40:30:302:soc",
            dict(
                naming_authority="2018-06.io.flexmeasures.staging",
                owner_id=40,
                asset_id=30,
                event_type="soc",
                event_id=302,
            ),
        ),
        (
            "event",
            "ea1.2018-06.io.flexmeasures.staging:30:302:soc",
            dict(
                naming_authority="2018-06.io.flexmeasures.staging",
                asset_id=30,
                owner_id=None,
                event_type="soc",
                event_id=302,
            ),
        ),
        (
            "connection",
            "ea1.2018-06.io.flexmeasures.staging:fm1.30",
            dict(
                naming_authority="2018-06.io.flexmeasures.staging",
                asset_id=30,
                owner_id=None,
                event_type="connection",
            ),
        ),
    ],
)
def test_parse_entity_address(entity_type, entity_address, exp_result):
    if isinstance(exp_result, str):  # this means we expect an exception
        with pytest.raises(EntityAddressException, match=exp_result):
            parse_entity_address(
                entity_address, entity_type=entity_type, fm_scheme="fm0"
            )
    else:
        res = parse_entity_address(
            entity_address, entity_type=entity_type, fm_scheme="fm0"
        )
        assert res["scheme"] == "ea1"
        assert res["naming_authority"] == exp_result["naming_authority"]
        if entity_type in ("connection", "event"):
            for field in ("asset_id", "owner_id"):
                assert res[field] == exp_result[field]
        if entity_type == "market":
            assert res["market_name"] == exp_result["market_name"]
        if entity_type == "weather_sensor":
            for field in ("weather_sensor_type_name", "latitude", "longitude"):
                assert res[field] == exp_result[field]
        if entity_type == "event":
            for field in ("event_type", "event_id"):
                assert res[field] == exp_result[field]


def test_reverse_domain_name():
    assert reverse_domain_name("flexmeasures.io") == "io.flexmeasures"
    assert reverse_domain_name("company.flexmeasures.io") == "io.flexmeasures.company"
    assert (
        reverse_domain_name("staging.company.flexmeasures.org")
        == "org.flexmeasures.company.staging"
    )
    assert (
        reverse_domain_name("staging.company.flexmeasures.io:4500")
        == "io.flexmeasures.company.staging"
    )
    assert (
        reverse_domain_name("https://staging.company.flexmeasures.io:4500")
        == "io.flexmeasures.company.staging"
    )
    assert (
        reverse_domain_name("https://user:pass@staging.company.flexmeasures.io:4500")
        == "io.flexmeasures.company.staging"
    )
    assert reverse_domain_name("test.flexmeasures.co.uk") == "uk.co.flexmeasures.test"
    assert (
        reverse_domain_name("test.staging.flexmeasures.co.uk")
        == "uk.co.flexmeasures.staging.test"
    )


def test_build_ea_scheme_and_naming_authority(app):
    assert build_ea_scheme_and_naming_authority(
        "localhost:5000"
    ) == "ea1.%s.localhost" % get_first_day_of_next_month().strftime("%Y-%m")
    assert (
        build_ea_scheme_and_naming_authority("flexmeasures.io")
        == "ea1.2021-01.io.flexmeasures"
    )
    with app.app_context():
        app.config["FLEXMEASURES_HOSTS_AND_AUTH_START"] = {
            "flexmeasures.io": "2021-01",
            "company.flexmeasures.io": "2020-04",
        }
        assert (
            build_ea_scheme_and_naming_authority("company.flexmeasures.io")
            == "ea1.2020-04.io.flexmeasures.company"
        )
    assert (
        build_ea_scheme_and_naming_authority("company.flexmeasures.io", "1999-12")
        == "ea1.1999-12.io.flexmeasures.company"
    )
    with pytest.raises(Exception, match="adhere to the format"):
        build_ea_scheme_and_naming_authority("company.flexmeasures.io", "1999--13")
    with pytest.raises(Exception, match="should be in the range"):
        build_ea_scheme_and_naming_authority("company.flexmeasures.io", "1999-13")
    with pytest.raises(Exception, match="when authority"):
        build_ea_scheme_and_naming_authority("company.notflexmeasures.io")
