# Copyright (C) 2023,2024,2025 Giovanni Fulco
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import constants

import upmplgutils
# uncomment when py-sonic with useragent support is released
from subsonic_connector.connector import Constants as SubsonicConnectorConstants
from subsonic_connector.configuration import ConfigurationInterface
from tag_type import TagType
from dotenv import dotenv_values
from album_property_key import AlbumPropertyKey


def get_plugin_config_variable_name(name: str) -> str:
    return f"{constants.PluginConstant.PLUGIN_NAME.value}{name}"


def _get_option_value(nm, dflt: any = None):
    return upmplgutils.getOptionValue(get_plugin_config_variable_name(nm), dflt)


def _get_option_value_as_bool(nm: str, default_value: int) -> bool:
    return _get_option_value(nm, default_value) == 1


def get_whitelist_codecs() -> list[str]:
    return str(get_config_param_as_str(constants.ConfigParam.WHITELIST_CODECS)).split(",")


# supported unless initializer understands it is not
# begin
album_list_by_highest_supported: bool = True
internet_radio_stations_supported: bool = True
# end


def is_transcode_enabled() -> bool:
    return (get_config_param_as_str(constants.ConfigParam.TRANSCODE_CODEC) or
            get_config_param_as_str(constants.ConfigParam.TRANSCODE_MAX_BITRATE))


def get_transcode_codec() -> str:
    transcode_codec: str = get_config_param_as_str(constants.ConfigParam.TRANSCODE_CODEC)
    if transcode_codec:
        return transcode_codec
    # only bitrate might be specified, in this case we return a fallback value for transcode codec
    if is_transcode_enabled():
        return constants.Defaults.FALLBACK_TRANSCODE_CODEC.value
    return None


def get_transcode_max_bitrate() -> int:
    transcode_max_bitrate: str = get_config_param_as_str(constants.ConfigParam.TRANSCODE_MAX_BITRATE)
    if transcode_max_bitrate:
        return int(transcode_max_bitrate)
    # if transcode codec is specified, we return the default, if available
    transcode_codec: str = get_config_param_as_str(constants.ConfigParam.TRANSCODE_CODEC)
    if transcode_codec:
        return constants.get_default_bitrate_by_codec(transcode_codec)
    # nothing to do
    return None


def is_tag_supported(tag: TagType) -> bool:
    # true unless there are exceptions ...
    if tag == TagType.HIGHEST_RATED_ALBUMS:
        return album_list_by_highest_supported
    elif tag == TagType.INTERNET_RADIOS:
        return internet_radio_stations_supported
    return True


def get_config_param_as_str(configuration_parameter: constants.ConfigParam) -> str:
    dv: str | None = configuration_parameter.default_value
    if dv is not None and not isinstance(dv, str):
        raise Exception(f"Invalid default value for [{configuration_parameter.key}]")
    v: any = _get_option_value(configuration_parameter.key, dv)
    if v is None:
        return None
    # v is set, check type!
    if isinstance(v, str):
        return v
    # try to convert to string
    return str(v)


def get_config_param_as_int(configuration_parameter: constants.ConfigParam) -> str:
    dv: int | None = configuration_parameter.default_value
    if dv is not None and not isinstance(dv, int):
        raise Exception(f"Invalid default value for [{configuration_parameter.key}]")
    v: any = _get_option_value(configuration_parameter.key, dv)
    if v is None:
        return None
    # v is set, check type!
    if isinstance(v, int):
        return v
    # try to convert to int
    return int(v)


def get_config_param_as_bool(configuration_parameter: constants.ConfigParam) -> bool:
    default_value_as_int: int = 0
    dv: any = configuration_parameter.default_value
    if isinstance(dv, int):
        default_value_as_int = 1 if dv == 1 else 0
    elif isinstance(dv, bool):
        default_value_as_int = 1 if dv else 0
    return _get_option_value_as_bool(
        configuration_parameter.key,
        default_value_as_int)


# shortcut for verbose logging
def get_verbose_logging() -> bool:
    return get_config_param_as_bool(constants.ConfigParam.VERBOSE_LOGGING)


# list of enabled AlbumPropertyKey
# transparent for now
def get_enabled_album_property_key_list() -> list[AlbumPropertyKey]:
    return list(AlbumPropertyKey)


def get_items_per_page() -> int:
    return min(
        constants.Defaults.SUBSONIC_API_MAX_RETURN_SIZE.value,
        get_config_param_as_int(constants.ConfigParam.ITEMS_PER_PAGE))


def getWebServerDocumentRoot() -> str:
    return upmplgutils.getUpnpWebDocRoot(constants.PluginConstant.PLUGIN_NAME.value)


def get_webserver_path_images() -> list[str]:
    return [
        constants.PluginConstant.PLUGIN_NAME.value,
        "images"]


def get_webserver_path_images_static() -> list[str]:
    return get_webserver_path_images() + ["static"]


def get_webserver_path_images_cache() -> list[str]:
    return get_webserver_path_images() + ["cache"]


def get_custom_headers() -> dict[str, str]:
    custom_headers: dict[str, str] = {}
    custom_headers_file_name: str = get_config_param_as_str(constants.ConfigParam.CUSTOM_HEADERS_FILE_NAME)
    # msgproc.log(f"UpmpdcliSubsonicConfig custom_headers_file_name [{custom_headers_file_name}]")
    if custom_headers_file_name:
        loaded_custom_headers: dict[str, str] = dotenv_values(custom_headers_file_name)
        custom_headers.update(loaded_custom_headers)
    return custom_headers


class UpmpdcliSubsonicConfig(ConfigurationInterface):

    def __init__(self):
        self.__custom_headers: dict[str, str] = get_custom_headers()

    def getBaseUrl(self) -> str: return _get_option_value("baseurl")

    def getPort(self) -> int: return _get_option_value("port")

    def getServerPath(self) -> int: return _get_option_value("serverpath")

    def getUserName(self) -> str: return _get_option_value("user")

    def getPassword(self) -> str: return _get_option_value("password")

    def getLegacyAuth(self) -> bool:
        legacy_auth_enabled_str: str = _get_option_value("legacyauth", "false")
        if legacy_auth_enabled_str.lower() not in ["true", "false", "1", "0"]:
            raise Exception(f"Invalid value for SUBSONIC_LEGACYAUTH [{legacy_auth_enabled_str}]")
        return legacy_auth_enabled_str in ["true", "1"]

    def getSalt(self) -> str:
        return None

    def getToken(self) -> str:
        return None

    def getUserAgent(self) -> bool:
        # only use empty user agent if explicitly instructed to do so
        if get_config_param_as_bool(constants.ConfigParam.SKIP_USER_AGENT):
            return None
        user_agent_str: str = get_config_param_as_str(constants.ConfigParam.USER_AGENT)
        if user_agent_str is None or len(user_agent_str) == 0:
            user_agent_str = constants.ConfigParam.USER_AGENT.default_value
        return user_agent_str

    def getCustomHeaders(self) -> dict[str, str]:
        return self.__custom_headers

    def getApiVersion(self) -> str: return SubsonicConnectorConstants.API_VERSION.value

    def getAppName(self) -> str: return "upmpdcli-subsonic-plugin"
