from packaging import version

from flask import current_app
from flexmeasures.data.models.generic_assets import GenericAsset, GenericAssetType
from flexmeasures import Source, __version__ as flexmeasures_version
from flexmeasures.data import db
from flexmeasures.data.services.data_sources import get_or_create_source

from flexmeasures_weather import DEFAULT_DATA_SOURCE_NAME
from flexmeasures_weather import WEATHER_STATION_TYPE_NAME
from flexmeasures_weather import DEFAULT_WEATHER_STATION_NAME


if version.parse(flexmeasures_version) < version.parse("0.13"):
    SOURCE_TYPE = "forecasting script"
else:
    SOURCE_TYPE = "forecaster"

FM_SUPPORTS_ACCOUNT_LINKED_SOURCES = version.parse(
    flexmeasures_version
) >= version.parse("0.32")

if FM_SUPPORTS_ACCOUNT_LINKED_SOURCES:
    from flexmeasures import Account
else:
    Account = None


def get_or_create_weather_account():
    """Make sure we have an account for the weather provider service."""
    if Account is None:
        raise RuntimeError(
            "FlexMeasures Account model is unavailable before FlexMeasures 0.32."
        )
    account_name = current_app.config.get(
        "WEATHER_DATA_SOURCE_NAME", DEFAULT_DATA_SOURCE_NAME
    )
    weather_account = Account.query.filter(
        Account.name == account_name,
    ).one_or_none()
    if weather_account is None:
        weather_account = Account(name=account_name)
        db.session.add(weather_account)
        db.session.flush()
    return weather_account


def get_or_create_owm_data_source() -> Source:
    """Make sure we have a weather provider data source of the configured type."""
    source_kwargs = dict(
        source=current_app.config.get(
            "WEATHER_DATA_SOURCE_NAME", DEFAULT_DATA_SOURCE_NAME
        ),
        source_type=SOURCE_TYPE,
        flush=False,
    )
    if FM_SUPPORTS_ACCOUNT_LINKED_SOURCES:
        source_kwargs["account"] = get_or_create_weather_account()
    return get_or_create_source(**source_kwargs)


def get_or_create_owm_data_source_for_derived_data() -> Source:
    owm_source_name = current_app.config.get(
        "WEATHER_DATA_SOURCE_NAME", DEFAULT_DATA_SOURCE_NAME
    )
    source_kwargs = dict(
        source=f"FlexMeasures {owm_source_name}",
        source_type=SOURCE_TYPE,
        flush=False,
    )
    if FM_SUPPORTS_ACCOUNT_LINKED_SOURCES:
        source_kwargs["account"] = get_or_create_weather_account()
    return get_or_create_source(**source_kwargs)


def get_or_create_weather_station_type() -> GenericAssetType:
    """Make sure a weather station type exists"""
    weather_station_type = GenericAssetType.query.filter(
        GenericAssetType.name == WEATHER_STATION_TYPE_NAME,
    ).one_or_none()
    if weather_station_type is None:
        weather_station_type = GenericAssetType(
            name=WEATHER_STATION_TYPE_NAME,
            description="A weather station with various sensors.",
        )
        db.session.add(weather_station_type)
    return weather_station_type


def get_or_create_weather_station(latitude: float, longitude: float) -> GenericAsset:
    """Make sure a weather station exists at this location."""
    station_name = current_app.config.get(
        "WEATHER_STATION_NAME", DEFAULT_WEATHER_STATION_NAME
    )
    weather_station = GenericAsset.query.filter(
        GenericAsset.latitude == latitude, GenericAsset.longitude == longitude
    ).one_or_none()
    if weather_station is None:
        weather_station_type = get_or_create_weather_station_type()
        weather_station = GenericAsset(
            name=station_name,
            generic_asset_type=weather_station_type,
            latitude=latitude,
            longitude=longitude,
        )
        db.session.add(weather_station)
    return weather_station


def get_weather_station_by_asset_id(asset_id: int) -> GenericAsset:
    weather_station = GenericAsset.query.filter(
        GenericAsset.generic_asset_type_id == asset_id
    ).one_or_none()
    if weather_station is None:
        raise Exception(
            f"[FLEXMEASURES-WEATHER] Weather station is not present for the given asset id '{asset_id}'."
        )

    if weather_station.latitude is None or weather_station.longitude is None:
        raise Exception(
            f"[FLEXMEASURES-WEATHER] Weather station {weather_station} is missing location information [Latitude, Longitude]."
        )

    return weather_station
