import logging
from typing import TypedDict

from src.internals.cache.redis import get_conn
from src.internals.database.database import cached_query, query_rowcount_db
from src.internals.serializers.artist import deserialize_artist
from src.lib.artist import get_artist
from src.lib.post import get_post_multiple


def get_favorite_artists(account_id, reload=False):
    key = f"favorite_artists:{account_id}"
    query = """
        SELECT
            aaf.id,
            aaf.service,
            aaf.artist_id,
            pam.added AS updated
        FROM
            account_artist_favorite AS aaf
            LEFT JOIN
            posts_added_max AS pam
            ON
                pam.service = aaf.service
                AND
                pam."user" = aaf.artist_id
        WHERE
            account_id = %s
        """
    user_favorite_artists = {
        (fav["service"], fav["artist_id"]): fav
        for fav in cached_query(
            query,
            key,
            (account_id,),
            reload=reload,
            lock_enabled=True,
        )
    }  # keep this in lookup so we can grab fields when next time

    # mget artists to prevent n+1, todo better mget with query integrated like posts
    keys = [f"artist:{service}:{artist_id}" for service, artist_id in user_favorite_artists.keys()]
    redis = get_conn()

    if keys:
        cache_result = (
            deserialize_artist(artist)
            for artist
            in (artist_str
                for artist_str
                in redis.mget(keys)
                if artist_str
            )
        )
    else:
        cache_result = []

    in_cache = {(artist["service"], artist["id"]): artist for artist in cache_result if artist}

    artists = []

    for favorite_artist in user_favorite_artists.values():
        artist = in_cache.get((favorite_artist["service"], favorite_artist["artist_id"]))

        if not artist:
            artist = get_artist(favorite_artist["service"], favorite_artist["artist_id"])

        if artist:
            artist["faved_seq"] = favorite_artist["id"]
            artist["last_imported"] = artist["updated"]
            artist["updated"] = favorite_artist["updated"]
            artists.append(artist)

    return artists

class TDFavoritePostData(TypedDict):
    id: str
    service: str
    artist_id: str
    post_id: str



def get_favorite_posts(account_id, reload=False):
    key = f"favorite_posts:{account_id}"
    query = """
        SELECT
            id,
            service,
            artist_id,
            post_id
        FROM
            account_post_favorite
        WHERE
            account_id = %s
    """
    favorites: list[TDFavoritePostData]= cached_query(query, key, (account_id,), reload=reload)

    posts_to_loads = {(f["service"], f["artist_id"], f["post_id"]): f for f in favorites}
    return_value = get_post_multiple(posts_to_loads)

    log_flag = False

    for post in return_value:
        if not post:
            log_flag = True
            continue

        post["faved_seq"] = posts_to_loads[(post["service"], post["user"], post["id"])]["id"]

    if log_flag:
        logging.exception("Fav post not found for account faves", extra={"account_id": account_id})

    return return_value


def add_favorite_artist(account_id, service, artist_id):
    query_rowcount_db(
        """
            INSERT INTO account_artist_favorite
                (
                    account_id,
                    service,
                    artist_id
                )
            VALUES
                (
                    %s,
                    %s,
                    %s
                )
            ON CONFLICT
                (account_id, service, artist_id) DO NOTHING
        """,
        (account_id, service, artist_id),
    )
    # g.connection.commit() we needed this before because we were in a transaction
    get_conn().delete(f"favorite_artists:{account_id}")
    # get_favorite_artists(account_id, True)


def add_favorite_post(account_id, service, artist_id, post_id):
    query = """
        INSERT INTO account_post_favorite (account_id, service, artist_id, post_id, created_at)
        SELECT %s, %s, %s, %s, current_timestamp
        WHERE EXISTS (
            SELECT 1
            FROM public.posts
            WHERE service = %s AND id = %s
        )
        ON CONFLICT (account_id, service, artist_id, post_id) DO NOTHING;
    """
    query_rowcount_db(
        query,
        (account_id, service, artist_id, post_id, service, post_id),
    )
    # g.connection.commit() we needed this before because we were in a transaction
    get_conn().delete(f"favorite_posts:{account_id}")
    # get_favorite_posts(account_id, True)


def remove_favorite_artist(account_id, service, artist_id):
    query_rowcount_db(
        "delete from account_artist_favorite where account_id = %s and service = %s and artist_id = %s",
        (account_id, service, artist_id),
    )
    # g.connection.commit() we needed this before because we were in a transaction
    get_conn().delete(f"favorite_artists:{account_id}")
    # get_favorite_artists(account_id, True)


def remove_favorite_post(account_id, service, artist_id, post_id):
    query_rowcount_db(
        "delete from account_post_favorite where account_id = %s and service = %s and artist_id = %s and post_id = %s",
        (account_id, service, artist_id, post_id),
    )
    # g.connection.commit() we needed this before because we were in a transaction
    get_conn().delete(f"favorite_posts:{account_id}")
    # get_favorite_posts(account_id, True)
