"""Canonical mapping for the X tweet-scraper post. canon(family, item) -> dict with the plan's keys.

Families: apidojo (tweet-scraper, twitter-scraper-lite), kaito (apidojo-shaped, 4 renames),
xquik (camelCase flat + nested author), danek (snake_case, shape differs per mode), badger (flat snake_case).
"""


def present(it, *path):
    cur = it
    for k in path:
        if not isinstance(cur, dict) or k not in cur:
            return False, None
        cur = cur[k]
    return True, cur


def first(it, *paths):
    for p in paths:
        ok, v = present(it, *p)
        if ok:
            return True, v
    return False, None


def num(v):
    try:
        return int(v)
    except (TypeError, ValueError):
        try:
            return float(v)
        except (TypeError, ValueError):
            return None


def canon(family, it):
    c = {}
    if family in ("apidojo", "kaito"):
        au = it.get("author") or {}
        c.update(id=it.get("id"), url=it.get("url"), text=it.get("fullText") or it.get("text"), created_at=it.get("createdAt"), lang=it.get("lang"),
                 author_username=au.get("userName"), author_name=au.get("name"), author_id=au.get("id"), author_followers=au.get("followers"),
                 likes=it.get("likeCount"), reposts=it.get("retweetCount"), replies=it.get("replyCount"), quotes=it.get("quoteCount"),
                 views=it.get("viewCount"), bookmarks=it.get("bookmarkCount"), is_reply=it.get("isReply"), conversation_id=it.get("conversationId"),
                 source=it.get("source"), is_long=None)
        if family == "apidojo":
            c["is_repost"], c["is_quote"] = it.get("isRetweet"), it.get("isQuote")
            rt = it.get("retweet") or {}
            c["_repost_original_len"] = len(rt.get("fullText") or rt.get("text") or "")
        else:
            c["is_repost"] = bool(it.get("retweeted_tweet")) if "retweeted_tweet" in it else None
            c["is_quote"] = bool(it.get("quoted_tweet")) if "quoted_tweet" in it else None
            rt = it.get("retweeted_tweet") if isinstance(it.get("retweeted_tweet"), dict) else {}
            c["_repost_original_len"] = len(rt.get("text") or "")
            c["_filler"] = it.get("type") == "mock_tweet"
        ok, m = first(it, ("extendedEntities", "media"), ("media",)); c["media"] = (m or []) if ok else None
        ok, u = present(it, "entities", "urls"); c["urls"] = (u or []) if ok else None
        ok, h = present(it, "entities", "hashtags"); c["hashtags"] = (h or []) if ok else None
    elif family == "xquik":
        au = it.get("author") or {}
        c.update(id=it.get("id"), url=it.get("url") or it.get("tweetUrl"), text=it.get("text"), created_at=it.get("createdAt"), lang=it.get("lang"),
                 author_username=it.get("authorUsername") or au.get("username"), author_name=it.get("authorName") or au.get("name"),
                 author_id=it.get("authorId") or au.get("id"), author_followers=it.get("authorFollowers") if "authorFollowers" in it else au.get("followers"),
                 likes=it.get("likeCount"), reposts=it.get("retweetCount"), replies=it.get("replyCount"), quotes=it.get("quoteCount"),
                 views=it.get("viewCount"), bookmarks=it.get("bookmarkCount"), is_reply=it.get("isReply"), is_quote=it.get("isQuoteStatus"),
                 conversation_id=it.get("conversationId"), source=it.get("source"), is_long=it.get("isNoteTweet"))
        # 2026-09-19: rows now carry an explicit isRetweet flag (absent on 2026-09-08, when only type="retweet" told)
        c["is_repost"] = it.get("isRetweet") if "isRetweet" in it else (True if it.get("type") in ("retweet", "repost") else None)
        rt = it.get("retweetedTweet") or it.get("retweetedStatus")
        c["_repost_original_len"] = len((rt or {}).get("text") or "") if isinstance(rt, dict) else 0
        ok, m = first(it, ("media",), ("entities", "media"), ("mediaUrls",)); c["media"] = (m or []) if ok else None
        ok, u = present(it, "entities", "urls"); c["urls"] = (u or []) if ok else None
        ok, h = present(it, "entities", "hashtags"); c["hashtags"] = (h or []) if ok else None
    elif family == "danek":
        ui = it.get("user_info") or it.get("author") or {}
        c.update(id=it.get("tweet_id") or it.get("id"), url=it.get("url"), text=it.get("text"), created_at=it.get("created_at"), lang=it.get("lang"),
                 author_username=ui.get("screen_name") or it.get("screen_name"), author_name=ui.get("name"), author_id=ui.get("rest_id"),
                 author_followers=ui.get("followers_count"), likes=it.get("favorites", it.get("likes")), reposts=it.get("retweets"),
                 replies=it.get("replies"), quotes=it.get("quotes"), views=num(it.get("views")), bookmarks=it.get("bookmarks"),
                 conversation_id=it.get("conversation_id"), source=it.get("source"), is_long=None)
        c["is_reply"] = bool(it.get("in_reply_to_status_id_str")) if "in_reply_to_status_id_str" in it else None
        c["is_repost"] = bool(it.get("retweeted_tweet") or it.get("retweeted")) if ("retweeted_tweet" in it or "retweeted" in it) else None
        c["is_quote"] = bool(it.get("quoted_tweet") or it.get("quoted")) if ("quoted_tweet" in it or "quoted" in it) else None
        c["_repost_original_len"] = len((it.get("retweeted_tweet") or {}).get("text") or "")
        c["media"] = it.get("media") if "media" in it else None
        ok, u = present(it, "entities", "urls"); c["urls"] = (u or []) if ok else None
        ok, h = present(it, "entities", "hashtags"); c["hashtags"] = (h or []) if ok else None
    elif family == "badger":
        c.update(id=it.get("id"), url=it.get("url"), text=it.get("full_text") or it.get("text"), created_at=it.get("created_at"), lang=it.get("lang"),
                 author_username=it.get("username"), author_name=it.get("user_name"), author_id=it.get("user_id"), author_followers=it.get("user_followers_count"),
                 likes=it.get("favorite_count"), reposts=it.get("retweet_count"), replies=it.get("reply_count"), quotes=it.get("quote_count"),
                 views=it.get("view_count"), bookmarks=it.get("bookmark_count"), is_repost=it.get("is_retweet"), is_quote=it.get("is_quote_status"),
                 conversation_id=it.get("conversation_id"), source=it.get("source"), is_long=None, _repost_original_len=0)
        c["is_reply"] = bool(it.get("in_reply_to_status_id")) if "in_reply_to_status_id" in it else None
        c["media"] = it.get("media") if "media" in it else None
        c["urls"] = it.get("urls") if "urls" in it else None
        c["hashtags"] = it.get("hashtags") if "hashtags" in it else None
    return c
