import json
from typing import Any, cast
from uuid import UUID

from alloy_runtime_types.dtos.knowledge import GetSynthesisResponse
from cli.infrastructure.formatting.fields import format_datetime
from cli.infrastructure.output import OutputService
from cli.infrastructure.renderers.entity_renderer import FieldConfig


def _format_optional_uuid(value: UUID | None) -> Any:
    if value is None:
        return cast(str, None)
    return str(value)


def _format_metadata(value: dict[str, Any] | None) -> Any:
    if value is None:
        return cast(str, None)
    return json.dumps(value, indent=2, sort_keys=True)


def _format_collection_ids(collection_ids: list[UUID]) -> str:
    if not collection_ids:
        return "[]"
    return "\n".join(str(collection_id) for collection_id in collection_ids)


def _format_optional_datetime(value: Any) -> Any:
    if value is None:
        return cast(str, None)
    return format_datetime(value)


def _queue_metadata_value(response: GetSynthesisResponse, key: str) -> Any:
    queue_metadata = response.queue_metadata
    if isinstance(queue_metadata, dict):
        queue_metadata_dict = cast(dict[str, Any], queue_metadata)
        return queue_metadata_dict.get(key)
    return getattr(queue_metadata, key)


def _format_request_fingerprint(response: GetSynthesisResponse) -> str:
    return cast(str, _queue_metadata_value(response, "request_fingerprint"))


def _has_attempt(response: GetSynthesisResponse) -> bool:
    attempt_number = _queue_metadata_value(response, "attempt_number")
    total_attempt_count = _queue_metadata_value(response, "total_attempt_count")
    return attempt_number is not None and total_attempt_count is not None


def _format_attempt(response: GetSynthesisResponse) -> str:
    attempt_number = _queue_metadata_value(response, "attempt_number")
    total_attempt_count = _queue_metadata_value(response, "total_attempt_count")
    return f"{attempt_number} of {total_attempt_count}"


def _has_failure_classification(response: GetSynthesisResponse) -> bool:
    return _queue_metadata_value(response, "failure_classification") is not None


def _format_failure_classification(response: GetSynthesisResponse) -> str:
    return cast(str, _queue_metadata_value(response, "failure_classification"))


def present_synthesis_details(response: GetSynthesisResponse) -> None:
    output = OutputService.get()

    fields = [
        FieldConfig("id", "ID", str),
        FieldConfig("title", "Title"),
        FieldConfig("topic", "Topic"),
        FieldConfig("status", "Status"),
        FieldConfig("is_stale", "Is Stale"),
        FieldConfig("staleness_reason", "Staleness Reason"),
        FieldConfig("source_chunk_count", "Source Chunks"),
        FieldConfig("collection_ids", "Collections", _format_collection_ids),
        FieldConfig(
            "synthesizer_agent_id",
            "Synthesizer Agent ID",
            _format_optional_uuid,
        ),
        FieldConfig("execution_id", "Execution ID", _format_optional_uuid),
        FieldConfig("error_message", "Error Message"),
        FieldConfig("metadata", "Metadata", _format_metadata),
        FieldConfig("synthesis_content", "Synthesis Content"),
        FieldConfig("created_at", "Created At", _format_optional_datetime),
        FieldConfig("updated_at", "Updated At", _format_optional_datetime),
    ]

    if response.queue_metadata is not None:
        queue_fields = [
            FieldConfig(
                "queue_metadata",
                "Request Fingerprint",
                lambda _value: _format_request_fingerprint(response),
            ),
        ]
        if _has_attempt(response):
            queue_fields.append(
                FieldConfig(
                    "queue_metadata",
                    "Attempt",
                    lambda _value: _format_attempt(response),
                )
            )
        if _has_failure_classification(response):
            queue_fields.append(
                FieldConfig(
                    "queue_metadata",
                    "Failure Classification",
                    lambda _value: _format_failure_classification(response),
                )
            )
        fields[10:10] = queue_fields

    output.entity(
        response,
        f"Synthesis: {response.title}",
        fields,
        raw_payload=response,
    )
