# retoor <retoor@molodetz.nl>

from .core import TTLCache, _in_clause, db, defaultdict


_comment_count_cache = TTLCache(ttl=15, max_size=10000)


def get_comment_counts_by_post_uids(post_uids):
    if not post_uids or "comments" not in db.tables:
        return {}
    result = {}
    misses = []
    for uid in post_uids:
        cached = _comment_count_cache.get(uid)
        if cached is None:
            misses.append(uid)
        else:
            result[uid] = cached
    if misses:
        placeholders, params = _in_clause(misses)
        rows = db.query(
            f"SELECT target_uid, COUNT(*) as c FROM comments WHERE target_type='post' AND target_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY target_uid",
            **params,
        )
        fetched = {r["target_uid"]: r["c"] for r in rows}
        for uid in misses:
            count = fetched.get(uid, 0)
            _comment_count_cache.set(uid, count)
            result[uid] = count
    return result


def get_post_counts_by_user_uids(user_uids):
    if not user_uids or "posts" not in db.tables:
        return {}
    placeholders, params = _in_clause(user_uids)
    rows = db.query(
        f"SELECT user_uid, COUNT(*) as c FROM posts WHERE user_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY user_uid",
        **params,
    )
    return {r["user_uid"]: r["c"] for r in rows}


def get_vote_counts(target_uids):
    if not target_uids or "votes" not in db.tables:
        return {}, {}
    placeholders, params = _in_clause(target_uids)
    rows = db.query(
        f"SELECT target_uid, value, COUNT(*) as c FROM votes WHERE target_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY target_uid, value",
        **params,
    )
    ups = {}
    downs = {}
    for r in rows:
        if r["value"] == 1:
            ups[r["target_uid"]] = r["c"]
        else:
            downs[r["target_uid"]] = r["c"]
    return ups, downs


def get_user_votes(user_uid, target_uids):
    if not user_uid or not target_uids or "votes" not in db.tables:
        return {}
    placeholders, params = _in_clause(target_uids)
    params["uid"] = user_uid
    rows = db.query(
        f"SELECT target_uid, value FROM votes WHERE user_uid = :uid AND target_uid IN ({placeholders}) AND deleted_at IS NULL",
        **params,
    )
    return {r["target_uid"]: r["value"] for r in rows}


def get_reactions_by_targets(target_type, target_uids, user=None):
    if not target_uids or "reactions" not in db.tables:
        return {}
    placeholders, params = _in_clause(target_uids)
    params["tt"] = target_type
    rows = db.query(
        f"SELECT target_uid, emoji, COUNT(*) as c FROM reactions WHERE target_type=:tt AND target_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY target_uid, emoji",
        **params,
    )
    counts = defaultdict(dict)
    for row in rows:
        counts[row["target_uid"]][row["emoji"]] = row["c"]
    mine = defaultdict(list)
    if user:
        placeholders, params = _in_clause(target_uids, prefix="m")
        params["tt"] = target_type
        params["u"] = user["uid"]
        for row in db.query(
            f"SELECT target_uid, emoji FROM reactions WHERE user_uid=:u AND target_type=:tt AND target_uid IN ({placeholders}) AND deleted_at IS NULL",
            **params,
        ):
            mine[row["target_uid"]].append(row["emoji"])
    result = {}
    for uid in target_uids:
        result[uid] = {
            "counts": dict(counts.get(uid, {})),
            "mine": list(mine.get(uid, [])),
        }
    return result


def get_user_bookmarks(user_uid, target_type, target_uids):
    if not user_uid or not target_uids or "bookmarks" not in db.tables:
        return set()
    placeholders, params = _in_clause(target_uids)
    params["u"] = user_uid
    params["tt"] = target_type
    rows = db.query(
        f"SELECT target_uid FROM bookmarks WHERE user_uid=:u AND target_type=:tt AND target_uid IN ({placeholders}) AND deleted_at IS NULL",
        **params,
    )
    return {row["target_uid"] for row in rows}


def get_polls_by_post_uids(post_uids, user=None):
    if not post_uids or "polls" not in db.tables:
        return {}
    placeholders, params = _in_clause(post_uids)
    polls = list(
        db.query(
            f"SELECT * FROM polls WHERE post_uid IN ({placeholders}) AND deleted_at IS NULL",
            **params,
        )
    )
    if not polls:
        return {}
    poll_uids = [poll["uid"] for poll in polls]
    option_placeholders, option_params = _in_clause(poll_uids, prefix="o")
    options = list(
        db.query(
            f"SELECT * FROM poll_options WHERE poll_uid IN ({option_placeholders}) AND deleted_at IS NULL ORDER BY position",
            **option_params,
        )
    )
    counts = defaultdict(dict)
    totals = defaultdict(int)
    if "poll_votes" in db.tables:
        vote_placeholders, vote_params = _in_clause(poll_uids, prefix="v")
        for row in db.query(
            f"SELECT poll_uid, option_uid, COUNT(*) as c FROM poll_votes WHERE poll_uid IN ({vote_placeholders}) AND deleted_at IS NULL GROUP BY poll_uid, option_uid",
            **vote_params,
        ):
            counts[row["poll_uid"]][row["option_uid"]] = row["c"]
            totals[row["poll_uid"]] += row["c"]
    user_choice = {}
    if user and "poll_votes" in db.tables:
        placeholders, params = _in_clause(poll_uids, prefix="m")
        params["u"] = user["uid"]
        for row in db.query(
            f"SELECT poll_uid, option_uid FROM poll_votes WHERE user_uid=:u AND poll_uid IN ({placeholders}) AND deleted_at IS NULL",
            **params,
        ):
            user_choice[row["poll_uid"]] = row["option_uid"]
    options_by_poll = defaultdict(list)
    for option in options:
        options_by_poll[option["poll_uid"]].append(option)
    result = {}
    for poll in polls:
        poll_uid = poll["uid"]
        total = totals.get(poll_uid, 0)
        rendered = []
        for option in options_by_poll.get(poll_uid, []):
            count = counts.get(poll_uid, {}).get(option["uid"], 0)
            rendered.append(
                {
                    "uid": option["uid"],
                    "label": option["label"],
                    "count": count,
                    "pct": round(count * 100 / total) if total else 0,
                }
            )
        result[poll["post_uid"]] = {
            "uid": poll_uid,
            "question": poll["question"],
            "options": rendered,
            "total": total,
            "my_choice": user_choice.get(poll_uid),
        }
    return result


def get_poll_for_post(post_uid, user=None):
    return get_polls_by_post_uids([post_uid], user).get(post_uid)


