#
# steering, Programme/schedule management for AURA
#
# Copyright (C) 2011-2017, 2020, Ernesto Rico Schmidt
# Copyright (C) 2017-2019, Ingo Leindecker
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import json
import typing
from datetime import date, datetime, time
from typing import Dict, Optional, Tuple, TypedDict, Union

import requests

from django.conf import settings
from django.utils import timezone

if typing.TYPE_CHECKING:
    from program.models import Host, HostLink, Note, NoteLink, Show, ShowLink


class Link(TypedDict):
    type_id: int
    url: str


def parse_datetime(date_string: str | None) -> datetime | None:
    """
    parse a datetime string and return a timezone aware datetime object.

    Returns `None` if no datetime string is given.
    """

    if date_string is None:
        return None

    try:
        parsed_datetime = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
    except ValueError:
        parsed_datetime = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S%z")

    return (
        timezone.make_aware(parsed_datetime)
        if timezone.is_naive(parsed_datetime)
        else parsed_datetime
    )


def parse_date(date_string: str | None) -> date | None:
    """
    parse a date string and return a date object.

    Returns `None` if no date string is given.
    """

    if date_string is None:
        return None

    return datetime.strptime(date_string, "%Y-%m-%d").date()


def parse_time(date_string: str | None) -> time | None:
    """
    parse a time string and return a time object.

    Returns `None` if no time string is given.
    """

    if date_string is None:
        return None

    if len(date_string) == 5:
        return datetime.strptime(date_string, "%H:%M").time()
    else:
        return datetime.strptime(date_string, "%H:%M:%S").time()


def get_audio_url(cba_id: Optional[int]) -> str:
    """
    Retrieve the direct URL to the mp3 in CBA
    In order to retrieve the URL, stations need
       - to be whitelisted by CBA
       - an API Key

    For these contact cba@fro.at
    """

    if not cba_id or settings.CBA_API_KEY == "":
        return ""
    else:
        if settings.DEBUG:
            url = (
                "https://cba.fro.at/wp-content/plugins/cba/ajax/cba-get-filename.php?post_id="
                + str(cba_id)
                + "&c=Ml3fASkfwR8"
            )
        else:
            url = (
                settings.CBA_AJAX_URL
                + "?action=cba_ajax_get_filename&post_id="
                + str(cba_id)
                + "&api_key="
                + settings.CBA_API_KEY
            )

        try:
            return requests.get(url).json()
        except (requests.RequestException, json.JSONDecodeError):
            # TODO: we might want to add some logging
            return ""


def get_values(
    kwargs: Dict[str, str], *keys: str
) -> Union[Tuple[Union[int, str, None], ...], int, str, None]:
    """Get the values of the keys from the kwargs."""

    def int_if_digit(value: Optional[str]) -> Optional[Union[int, str]]:
        return int(value) if value and value.isdigit() else value

    values = [kwargs.get(key) for key in keys]

    if len(values) > 1:
        return tuple(int_if_digit(value) for value in values)
    else:
        return int_if_digit(values[0])


def update_links(
    instance: Union["Host", "Note", "Show"], links: list[Link]
) -> Union["Host", "Note", "Show"]:
    """Update the links associated with the instance"""

    # delete the links associated with the instance
    if instance.links.count() > 0:
        for link in instance.links.all():
            link.delete(keep_parents=True)

    match type(instance):
        case "Host":
            for link_data in links:
                HostLink.objects.create(host=instance, **link_data)
        case "Note":
            for link_data in links:
                NoteLink.objects.create(note=instance, **link_data)
        case "Show":
            for link_data in links:
                ShowLink.objects.create(show=instance, **link_data)

    return instance