from __future__ import annotations

import json
from typing import Any


def format_data_models_overview(data_models: list) -> str:
    """Format a list of data models into a JSON overview (names + views)."""
    result = []
    for dm in data_models:
        views_summary = []
        if hasattr(dm, "views") and dm.views:
            for view in dm.views:
                view_info: dict[str, Any] = {
                    "space": view.space,
                    "externalId": view.external_id,
                    "version": view.version,
                }
                if hasattr(view, "description") and view.description:
                    view_info["description"] = view.description
                if hasattr(view, "name") and view.name:
                    view_info["name"] = view.name
                views_summary.append(view_info)

        result.append(
            {
                "space": dm.space,
                "externalId": dm.external_id,
                "version": dm.version,
                "name": getattr(dm, "name", None),
                "description": getattr(dm, "description", None),
                "views": views_summary,
            }
        )
    return json.dumps(result, indent=2, default=str)


def format_view_schema(view: Any) -> str:
    """Format a single view with full property details."""
    return json.dumps(_format_view(view), indent=2, default=str)


def _format_view(view: Any) -> dict[str, Any]:
    """Convert a View object to a dict with property details."""
    properties = {}
    if hasattr(view, "properties") and view.properties:
        for prop_name, prop in view.properties.items():
            prop_info: dict[str, Any] = {}
            if hasattr(prop, "dump"):
                prop_info = prop.dump(camel_case=True)
            else:
                prop_info = {
                    "type": str(type(prop).__name__),
                }
                if hasattr(prop, "description"):
                    prop_info["description"] = prop.description
                if hasattr(prop, "nullable"):
                    prop_info["nullable"] = prop.nullable
            properties[prop_name] = prop_info

    implements = None
    if hasattr(view, "implements") and view.implements:
        implements = [
            {
                "space": v.space,
                "externalId": v.external_id,
                "version": v.version,
            }
            for v in view.implements
        ]

    return {
        "space": view.space,
        "externalId": view.external_id,
        "version": view.version,
        "name": getattr(view, "name", None),
        "description": getattr(view, "description", None),
        "properties": properties,
        "implements": implements,
    }


def format_instances(items: list, total: int | None = None) -> str:
    """Format instance results as JSON."""
    serialized = []
    for item in items:
        if hasattr(item, "dump"):
            serialized.append(item.dump(camel_case=True))
        elif hasattr(item, "properties"):
            entry: dict[str, Any] = {
                "space": getattr(item, "space", None),
                "externalId": getattr(item, "external_id", None),
                "instanceType": getattr(item, "instance_type", None),
            }
            # Properties are typically nested by source (view)
            if item.properties:
                props: dict[str, Any] = {}
                for source_id, source_props in item.properties.items():
                    key = str(source_id)
                    props[key] = dict(source_props) if source_props else {}
                entry["properties"] = props
            serialized.append(entry)
        else:
            serialized.append(str(item))

    result: dict[str, Any] = {"items": serialized, "count": len(serialized)}
    if total is not None:
        result["total"] = total
        result["message"] = f"Showing {len(serialized)} of {total} instances."

    return json.dumps(result, indent=2, default=str)


def format_query_results(query_result: Any) -> str:
    """Format query results (multiple result sets).

    QueryResult is a UserDict: keys are result set names,
    values are NodeListWithCursor/EdgeListWithCursor (which have .dump()).
    """
    result: dict[str, Any] = {}

    for name, items in query_result.items():
        if hasattr(items, "dump"):
            # NodeListWithCursor/EdgeListWithCursor.dump() returns list[dict]
            serialized = items.dump(camel_case=True)
        else:
            serialized = [str(item) for item in items]
        result[name] = {
            "items": serialized,
            "count": len(serialized),
        }
        # Include cursor if present for pagination
        if hasattr(items, "cursor") and items.cursor:
            result[name]["cursor"] = items.cursor

    return json.dumps(result, indent=2, default=str)


def format_aggregation(agg_result: Any, hint: str | None = None) -> str:
    """Format aggregation results."""
    results = []
    if hasattr(agg_result, "__iter__"):
        for item in agg_result:
            if hasattr(item, "dump"):
                results.append(item.dump(camel_case=True))
            else:
                entry: dict[str, Any] = {}
                for attr in ["value", "count", "aggregates", "group"]:
                    if hasattr(item, attr):
                        val = getattr(item, attr)
                        entry[attr] = val
                results.append(entry)
    else:
        results.append(str(agg_result))

    if hint:
        output: dict[str, Any] = {"results": results, "hint": hint}
        return json.dumps(output, indent=2, default=str)
    return json.dumps(results, indent=2, default=str)
