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 _clean_prop_info(prop_info: dict[str, Any]) -> dict[str, Any]:
    """Strip SDK internals that mislead LLMs into using wrong property references.

    The SDK dump includes ``container`` / ``containerPropertyIdentifier`` which
    refer to the *storage* container, not the view the user is querying.  LLMs
    frequently copy those identifiers into filter property references, producing
    "Unknown property" errors.
    """
    strip_keys = {"container", "containerPropertyIdentifier", "immutable", "autoIncrement", "constraintState"}
    cleaned = {k: v for k, v in prop_info.items() if k not in strip_keys}
    if isinstance(cleaned.get("type"), dict):
        cleaned["type"] = {k: v for k, v in cleaned["type"].items() if k != "container"}
    return cleaned


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 = _clean_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
        ]

    view_space = view.space
    view_ext_id = view.external_id
    view_ver = view.version

    return {
        "space": view_space,
        "externalId": view_ext_id,
        "version": view_ver,
        "name": getattr(view, "name", None),
        "description": getattr(view, "description", None),
        "propertyReference": f'["{view_space}", "{view_ext_id}/{view_ver}", "<propertyName>"]',
        "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 _serialize_aggregation_item(item: Any) -> Any:
    """Serialize a single aggregation item to a JSON-compatible structure."""
    if hasattr(item, "dump"):
        return item.dump(camel_case=True)

    if isinstance(item, dict):
        return item

    entry: dict[str, Any] = {}
    for attr in ["value", "count", "aggregates", "group"]:
        if hasattr(item, attr):
            entry[attr] = getattr(item, attr)

    if entry:
        return entry

    return str(item)


def format_aggregation(agg_result: Any, hint: str | None = None) -> str:
    """Format aggregation results."""
    if hasattr(agg_result, "__iter__") and not isinstance(agg_result, (str, bytes, dict)):
        results = [_serialize_aggregation_item(item) for item in agg_result]
    else:
        serialized = _serialize_aggregation_item(agg_result)
        results = serialized if isinstance(serialized, list) else [serialized]

    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)


def format_document_answer(answer: Any) -> str:
    """Format AI document question answering output as JSON."""
    if hasattr(answer, "content") and isinstance(answer.content, list):
        content: list[dict[str, Any]] = []
        for part in answer.content:
            references: list[dict[str, Any]] = []
            for ref in getattr(part, "references", []) or []:
                entry: dict[str, Any] = {
                    "fileId": getattr(ref, "file_id", None),
                    "externalId": getattr(ref, "external_id", None),
                    "fileName": getattr(ref, "file_name", None),
                    "locations": [
                        {
                            "pageNumber": getattr(loc, "page_number", None),
                            "left": getattr(loc, "left", None),
                            "right": getattr(loc, "right", None),
                            "top": getattr(loc, "top", None),
                            "bottom": getattr(loc, "bottom", None),
                        }
                        for loc in (getattr(ref, "locations", []) or [])
                    ],
                }

                instance_id = getattr(ref, "instance_id", None)
                if instance_id is not None:
                    entry["instanceId"] = {
                        "space": getattr(instance_id, "space", None),
                        "externalId": getattr(instance_id, "external_id", None),
                    }
                references.append(entry)

            content.append(
                {
                    "text": getattr(part, "text", ""),
                    "references": references,
                }
            )
        serialized: Any = {"content": content}
    elif hasattr(answer, "dump"):
        serialized = answer.dump(camel_case=True)
    elif isinstance(answer, list):
        serialized = [
            item.dump(camel_case=True) if hasattr(item, "dump") else item
            for item in answer
        ]
    else:
        serialized = answer

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


def format_document_summary(summary: Any) -> str:
    """Format AI document summarization output as JSON."""
    if hasattr(summary, "dump"):
        serialized: Any = summary.dump(camel_case=True)
    elif isinstance(summary, dict):
        serialized = summary
    elif isinstance(summary, str):
        serialized = {"summary": summary}
    else:
        serialized = {"summary": getattr(summary, "summary", summary)}

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