from __future__ import annotations

from copy import deepcopy
from typing import Any, Literal, cast
from uuid import uuid4

from backend.constants import DOCX_MIME_TYPE, PPTX_MIME_TYPE
from backend.schemas.generated_document import (
    AgentHubDocumentMetadata,
    LineageRole,
    RevisionStatus,
)
from backend.services.artifact_models import ArtifactReference

DOCUMENT_METADATA_KEY = "agentHubDocumentMetadata"
LINEAGE_ROLE_CURRENT: LineageRole = "current"
LINEAGE_ROLE_PROPOSED: LineageRole = "proposed"
LINEAGE_ROLE_HISTORY: LineageRole = "history"
REVISION_STATUS_PENDING: RevisionStatus = "pending"
REVISION_STATUS_ACCEPTED: RevisionStatus = "accepted"
REVISION_STATUS_REJECTED: RevisionStatus = "rejected"
REVISION_STATUS_SUPERSEDED: RevisionStatus = "superseded"
REVIEWABLE_REVISION_STATUSES: set[RevisionStatus] = {REVISION_STATUS_ACCEPTED, REVISION_STATUS_REJECTED}
GeneratedDocumentFormat = Literal["pptx", "docx"]
GENERATED_DOCUMENT_FORMAT_PPTX: GeneratedDocumentFormat = "pptx"
GENERATED_DOCUMENT_FORMAT_DOCX: GeneratedDocumentFormat = "docx"


def build_initial_document_metadata(lineage_id: str | None = None) -> AgentHubDocumentMetadata:
    return {
        "lineageId": lineage_id or str(uuid4()),
        "lineageRole": LINEAGE_ROLE_CURRENT,
        "revisionStatus": REVISION_STATUS_ACCEPTED,
    }


def build_revision_document_metadata(
    *,
    lineage_id: str,
    source: ArtifactReference,
    source_lineage_role: LineageRole,
) -> AgentHubDocumentMetadata:
    source_revision_status = (
        REVISION_STATUS_ACCEPTED if source_lineage_role == LINEAGE_ROLE_CURRENT else REVISION_STATUS_PENDING
    )
    return {
        "lineageId": lineage_id,
        "lineageRole": LINEAGE_ROLE_PROPOSED,
        "revisionStatus": REVISION_STATUS_PENDING,
        "source": source.as_metadata(),
        "_sourceState": {
            "lineageRole": source_lineage_role,
            "revisionStatus": source_revision_status,
        },
    }


def get_document_metadata(artifact: dict[str, Any] | None) -> AgentHubDocumentMetadata | None:
    if not isinstance(artifact, dict):
        return None
    metadata = artifact.get(DOCUMENT_METADATA_KEY)
    return cast(AgentHubDocumentMetadata, metadata) if isinstance(metadata, dict) else None


def extract_pptx_deck_yaml(part: dict[str, Any]) -> str | None:
    """Recover retained editable deck YAML from a generated PPTX inline part."""

    if part.get("type") != "DATA_INLINE" or part.get("mimeType") != PPTX_MIME_TYPE:
        return None
    generation_metadata = part.get("agentHubGenerationMetadata")
    if not isinstance(generation_metadata, dict):
        return None
    deck_yaml = generation_metadata.get("deckYaml")
    if not isinstance(deck_yaml, str) or not deck_yaml.strip():
        return None
    return deck_yaml


def extract_pptx_image_assets(part: dict[str, Any]) -> dict[str, dict[str, Any]] | None:
    """Read persisted image-asset bindings from one generated PPTX inline part."""

    if part.get("type") != "DATA_INLINE" or part.get("mimeType") != PPTX_MIME_TYPE:
        return None
    generation_metadata = part.get("agentHubGenerationMetadata")
    if not isinstance(generation_metadata, dict):
        return None
    image_assets = generation_metadata.get("imageAssets")
    if not isinstance(image_assets, dict):
        return None
    normalized_assets: dict[str, dict[str, Any]] = {}
    for asset_id, metadata in image_assets.items():
        if not isinstance(asset_id, str) or not asset_id.strip():
            continue
        if not isinstance(metadata, dict):
            continue
        normalized_assets[asset_id] = metadata
    return normalized_assets or None


def extract_pptx_deck_yaml_from_artifact(artifact: dict[str, Any]) -> str | None:
    """Find retained editable deck YAML attached to a generated PPTX artifact."""

    for part in artifact.get("parts") or []:
        if isinstance(part, dict):
            deck_yaml = extract_pptx_deck_yaml(part)
            if deck_yaml is not None:
                return deck_yaml
    return None


def extract_pptx_image_assets_from_artifact(artifact: dict[str, Any]) -> dict[str, dict[str, Any]] | None:
    """Find retained image asset bindings attached to a generated PPTX artifact."""

    for part in artifact.get("parts") or []:
        if isinstance(part, dict):
            image_assets = extract_pptx_image_assets(part)
            if image_assets is not None:
                return image_assets
    return None


def extract_generated_document_image_assets_from_artifact(
    artifact: dict[str, Any],
    document_format: GeneratedDocumentFormat,
) -> dict[str, dict[str, Any]] | None:
    """Find retained image bindings on the inline part for the requested generated-document format."""

    expected_mime_type = PPTX_MIME_TYPE if document_format == GENERATED_DOCUMENT_FORMAT_PPTX else DOCX_MIME_TYPE
    for part in artifact.get("parts") or []:
        if not isinstance(part, dict):
            continue
        if part.get("type") != "DATA_INLINE" or part.get("mimeType") != expected_mime_type:
            continue
        generation_metadata = part.get("agentHubGenerationMetadata")
        if not isinstance(generation_metadata, dict):
            continue
        image_assets = generation_metadata.get("imageAssets")
        if not isinstance(image_assets, dict):
            continue
        normalized_assets = {
            asset_id: metadata
            for asset_id, metadata in image_assets.items()
            if isinstance(asset_id, str) and asset_id.strip() and isinstance(metadata, dict)
        }
        return normalized_assets or None
    return None


def extract_docx_document_yaml(part: dict[str, Any]) -> str | None:
    """Recover retained editable document YAML from a generated DOCX inline part."""

    if part.get("type") != "DATA_INLINE" or part.get("mimeType") != DOCX_MIME_TYPE:
        return None
    generation_metadata = part.get("agentHubGenerationMetadata")
    if not isinstance(generation_metadata, dict):
        return None
    document_yaml = generation_metadata.get("documentYaml")
    if not isinstance(document_yaml, str) or not document_yaml.strip():
        return None
    return document_yaml


def extract_docx_document_yaml_from_artifact(artifact: dict[str, Any]) -> str | None:
    """Find retained editable document YAML attached to a generated DOCX artifact."""

    for part in artifact.get("parts") or []:
        if isinstance(part, dict):
            document_yaml = extract_docx_document_yaml(part)
            if document_yaml is not None:
                return document_yaml
    return None


def extract_generated_document_source_yaml_from_artifact(
    artifact: dict[str, Any], document_format: GeneratedDocumentFormat
) -> str | None:
    """Recover the retained editable YAML for one supported generated-document format."""

    if document_format == GENERATED_DOCUMENT_FORMAT_PPTX:
        return extract_pptx_deck_yaml_from_artifact(artifact)
    return extract_docx_document_yaml_from_artifact(artifact)


def detect_generated_document_format(artifact: dict[str, Any]) -> GeneratedDocumentFormat | None:
    """Detect exactly one retained editable generated-document format."""

    formats = [
        document_format
        for document_format in (GENERATED_DOCUMENT_FORMAT_PPTX, GENERATED_DOCUMENT_FORMAT_DOCX)
        if extract_generated_document_source_yaml_from_artifact(artifact, document_format) is not None
    ]
    return formats[0] if len(formats) == 1 else None


def extract_generated_document_filename_from_artifact(
    artifact: dict[str, Any], document_format: GeneratedDocumentFormat
) -> str | None:
    """Return the filename from the inline part that matches the expected generated-document format."""

    expected_mime_type = PPTX_MIME_TYPE if document_format == GENERATED_DOCUMENT_FORMAT_PPTX else DOCX_MIME_TYPE
    for part in artifact.get("parts") or []:
        if not isinstance(part, dict):
            continue
        if part.get("type") != "DATA_INLINE" or part.get("mimeType") != expected_mime_type:
            continue
        filename = part.get("filename")
        if isinstance(filename, str) and filename.strip():
            return filename.strip()
    return None


def copy_artifacts_metadata(artifacts_metadata: dict[str, Any] | None) -> dict[str, Any]:
    return deepcopy(artifacts_metadata or {})


def iter_document_artifacts(
    artifacts_metadata: dict[str, Any] | None,
):
    for artifacts_id, metadata in (artifacts_metadata or {}).items():
        artifacts = metadata.get("artifacts") if isinstance(metadata, dict) else None
        if not isinstance(artifacts, list):
            continue
        for artifact_index, artifact in enumerate(artifacts):
            document_metadata = get_document_metadata(artifact)
            if document_metadata:
                yield artifacts_id, artifact_index, artifact, document_metadata


def make_history_marker(artifact: dict[str, Any], *, revision_status: RevisionStatus | None = None) -> None:
    """Discard materialized content while preserving a stable artifact shell and review history."""

    document_metadata = get_document_metadata(artifact)
    if document_metadata is None:
        raise ValueError("Generated document metadata is required to create a history marker")

    for part in artifact.get("parts") or []:
        if not isinstance(part, dict):
            continue
        filename = part.get("filename")
        mime_type = part.get("mimeType")
        if isinstance(filename, str) and filename.strip():
            document_metadata.setdefault("filename", filename.strip())
        if isinstance(mime_type, str) and mime_type.strip():
            document_metadata.setdefault("mimeType", mime_type.strip())
        if document_metadata.get("filename") or document_metadata.get("mimeType"):
            break

    artifact["parts"] = []
    document_metadata["lineageRole"] = LINEAGE_ROLE_HISTORY
    if revision_status is not None:
        document_metadata["revisionStatus"] = revision_status


def is_history_marker(artifact: dict[str, Any] | None) -> bool:
    metadata = get_document_metadata(artifact)
    return metadata is not None and metadata.get("lineageRole") == LINEAGE_ROLE_HISTORY


def is_materialized_document_artifact(artifact: dict[str, Any] | None) -> bool:
    metadata = get_document_metadata(artifact)
    return metadata is not None and metadata.get("lineageRole") in {
        LINEAGE_ROLE_CURRENT,
        LINEAGE_ROLE_PROPOSED,
    }


def refresh_artifacts_group_metadata(artifacts_metadata: dict[str, Any], artifacts_id: str) -> None:
    group = artifacts_metadata.get(artifacts_id)
    if not isinstance(group, dict):
        return

    artifacts = group.get("artifacts")
    if not isinstance(artifacts, list):
        return

    from backend.utils.events_utils import get_artifacts_size_mb, has_inline, has_records

    group["size_mb"] = get_artifacts_size_mb(artifacts)
    group["has_records"] = has_records(artifacts) or has_inline(artifacts)
    if not any((artifact.get("parts") or []) for artifact in artifacts if isinstance(artifact, dict)):
        group["preview"] = False


def message_artifacts_metadata(message: Any) -> dict[str, Any] | None:
    if isinstance(message, dict):
        value = message.get("artifacts_metadata") or message.get("artifactsMetadata") or message.get("artifacts")
        return value if isinstance(value, dict) else None
    value = getattr(message, "artifacts_metadata", None) or getattr(message, "artifacts", None)
    return value if isinstance(value, dict) else None
