from __future__ import annotations

from typing import Any, Callable


def prepare_table_data(command: str, data: Any) -> Any:
    projector = _PROJECTORS.get(command)
    if projector is None:
        return data
    try:
        return projector(data)
    except Exception:
        return data


def _project_note_search(data: Any) -> list[dict[str, Any]]:
    rows = _normalize_rows(data)
    projected = []
    for item in rows:
        note_card = _as_dict(item.get("note_card"))
        user = _as_dict(note_card.get("user"))
        interact_info = _as_dict(note_card.get("interact_info"))
        projected.append(
            {
                "id": _first_present(item.get("id"), item.get("note_id")),
                "title": _first_present(note_card.get("display_title"), item.get("title")),
                "author": _first_present(user.get("nickname"), user.get("nick_name"), item.get("author")),
                "note_type": _first_present(note_card.get("type"), item.get("note_type")),
                "publish_time": _extract_publish_time(note_card),
                "liked_count": interact_info.get("liked_count"),
                "collected_count": interact_info.get("collected_count"),
                "comment_count": interact_info.get("comment_count"),
                "shared_count": interact_info.get("shared_count"),
                "note_url": item.get("note_url"),
                "xsec_token": item.get("xsec_token"),
            }
        )
    return _strip_empty_columns(projected)


def _project_user_search(data: Any) -> list[dict[str, Any]]:
    rows = _normalize_rows(data)
    projected = []
    for item in rows:
        projected.append(
            {
                "id": _first_present(item.get("user_id"), item.get("id")),
                "name": _first_present(item.get("name"), item.get("nickname")),
                "red_id": item.get("red_id"),
                "fans": item.get("fans"),
                "note_count": item.get("note_count"),
                "profession": item.get("profession"),
                "verified": _first_present(
                    item.get("red_official_verified"),
                    item.get("show_red_official_verify_icon"),
                ),
                "followed": item.get("followed"),
                "is_self": item.get("is_self"),
                "update_time": item.get("update_time"),
                "user_url": item.get("user_url"),
                "xsec_token": item.get("xsec_token"),
            }
        )
    return _strip_empty_columns(projected)


def _project_note_detail(data: Any) -> dict[str, Any]:
    item = _as_dict(data)
    user = _as_dict(item.get("user"))
    interact_info = _as_dict(item.get("interact_info"))
    comments = item.get("comments")
    return _strip_empty_values(
        {
            "id": _first_present(item.get("note_id"), item.get("id")),
            "title": _first_present(item.get("title"), item.get("display_title")),
            "author": _first_present(user.get("nickname"), user.get("nick_name")),
            "note_type": item.get("type"),
            "liked_count": interact_info.get("liked_count"),
            "collected_count": interact_info.get("collected_count"),
            "comment_count": _first_present(
                interact_info.get("comment_count"),
                len(comments) if isinstance(comments, list) else None,
            ),
            "shared_count": interact_info.get("shared_count"),
            "ip_location": item.get("ip_location"),
            "note_url": item.get("note_url"),
            "xsec_token": item.get("xsec_token"),
        }
    )


def _project_user_detail(data: Any) -> dict[str, Any]:
    item = _as_dict(data)
    counts = _extract_interaction_counts(item.get("interactions"))
    notes = item.get("notes")
    return _strip_empty_values(
        {
            "user_id": _first_present(item.get("user_id"), item.get("id")),
            "nickname": _first_present(item.get("nickname"), item.get("name")),
            "red_id": item.get("red_id"),
            "fans": counts.get("fans"),
            "follows": counts.get("follows"),
            "interaction": counts.get("interaction"),
            "gender": item.get("gender"),
            "ip_location": item.get("ip_location"),
            "notes_count": len(notes) if isinstance(notes, list) else None,
            "user_url": item.get("user_url"),
            "xsec_token": item.get("xsec_token"),
        }
    )


def _project_user_self(data: Any) -> dict[str, Any]:
    item = _as_dict(data)
    counts = _extract_interaction_counts(item.get("interactions"))
    return _strip_empty_values(
        {
            "user_id": _first_present(item.get("user_id"), item.get("id")),
            "nickname": item.get("nickname"),
            "red_id": item.get("red_id"),
            "fans": counts.get("fans"),
            "follows": counts.get("follows"),
            "interaction": counts.get("interaction"),
            "gender": item.get("gender"),
            "guest": item.get("guest"),
            "ip_location": item.get("ip_location"),
        }
    )


def _project_doctor(data: Any) -> list[dict[str, Any]]:
    item = _as_dict(data)
    rows = []
    overall_ok = item.get("overall_ok")
    if overall_ok is not None:
        rows.append(
            {
                "check": "overall",
                "ok": overall_ok,
                "summary": "all checks passed" if overall_ok else "one or more checks failed",
            }
        )
    for check_name, value in item.items():
        if check_name == "overall_ok":
            continue
        if isinstance(value, dict):
            rows.append(
                {
                    "check": check_name,
                    "ok": value.get("ok"),
                    "summary": _summarize_mapping(value, exclude_keys={"ok"}),
                }
            )
            continue
        rows.append({"check": check_name, "summary": value})
    return rows


def _normalize_rows(data: Any) -> list[dict[str, Any]]:
    if isinstance(data, list):
        return [item for item in data if isinstance(item, dict)]
    if isinstance(data, dict):
        return [data]
    return []


def _strip_empty_columns(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    if not rows:
        return rows
    columns = {
        key
        for row in rows
        for key, value in row.items()
        if _has_value(value)
    }
    return [{key: row.get(key) for key in columns} for row in rows]


def _strip_empty_values(data: dict[str, Any]) -> dict[str, Any]:
    return {key: value for key, value in data.items() if _has_value(value)}


def _extract_publish_time(note_card: dict[str, Any]) -> Any:
    corner_tags = note_card.get("corner_tag_info")
    if not isinstance(corner_tags, list):
        return None
    for item in corner_tags:
        if not isinstance(item, dict):
            continue
        if item.get("type") == "publish_time":
            return item.get("text")
    return None


def _extract_interaction_counts(interactions: Any) -> dict[str, Any]:
    if not isinstance(interactions, list):
        return {}
    result: dict[str, Any] = {}
    for item in interactions:
        if not isinstance(item, dict):
            continue
        interaction_type = item.get("type")
        if not isinstance(interaction_type, str) or not interaction_type:
            continue
        result[interaction_type] = item.get("count")
    return result


def _summarize_mapping(data: dict[str, Any], *, exclude_keys: set[str] | None = None) -> str:
    exclude = exclude_keys or set()
    parts = []
    for key, value in data.items():
        if key in exclude or not _has_value(value):
            continue
        parts.append(f"{key}={value}")
        if len(parts) >= 4:
            break
    if not parts:
        return ""
    remaining = len([key for key, value in data.items() if key not in exclude and _has_value(value)]) - len(parts)
    if remaining > 0:
        parts.append(f"+{remaining} more")
    return ", ".join(parts)


def _as_dict(value: Any) -> dict[str, Any]:
    return value if isinstance(value, dict) else {}


def _first_present(*values: Any) -> Any:
    for value in values:
        if _has_value(value):
            return value
    return None


def _has_value(value: Any) -> bool:
    if value is None:
        return False
    if isinstance(value, str):
        return bool(value.strip())
    return True


_PROJECTORS: dict[str, Callable[[Any], Any]] = {
    "doctor": _project_doctor,
    "doctor.run": _project_doctor,
    "note.get": _project_note_detail,
    "note.search": _project_note_search,
    "user.get": _project_user_detail,
    "user.search": _project_user_search,
    "user.self": _project_user_self,
}
