"""
Canonical metadata shape for DB and API.

Metadata is stored and returned in a single template shape:
- ownership: object with business_owners, bu_sme, it_application_owners, it_sme, support_lead (lists)
- lineage: object with pulls_from, pushes_to, system_sources (lists)
- runtime: object with storage_locations, serve, environments (optional)
"""

from __future__ import annotations

from typing import Any

# Keys we sync to join tables (ownership roles, lineage, domain)
OWNERSHIP_KEYS = (
    "business_owners",
    "bu_sme",
    "it_application_owners",
    "it_sme",
    "support_lead",
)
LINEAGE_KEYS = ("pulls_from", "pushes_to", "system_sources")


def _get_runtime(metadata: dict[str, Any]) -> dict[str, Any]:
    """Return runtime dict from metadata, merging top-level storage_locations/serve/environments if present."""
    runtime = metadata.get("runtime")
    if isinstance(runtime, dict):
        out = dict(runtime)
    else:
        out = {}
    # Backward compat: pull from root into runtime so canonical has one shape
    if "storage_locations" not in out and "storage_locations" in metadata:
        sl = metadata["storage_locations"]
        out["storage_locations"] = sl if isinstance(sl, list) else []
    if "serve" not in out and "serve" in metadata:
        out["serve"] = (
            metadata["serve"] if isinstance(metadata.get("serve"), dict) else {}
        )
    if "environments" not in out and "environments" in metadata:
        env = metadata["environments"]
        out["environments"] = env if isinstance(env, list) else []
    out.setdefault("storage_locations", [])
    out.setdefault("serve", {})
    out.setdefault("environments", [])
    return out


def ensure_canonical(metadata: dict[str, Any] | None) -> dict[str, Any]:
    """
    Return metadata in canonical shape. Does not mutate input.

    Ensures ownership, lineage, and runtime are dicts; other keys are copied as-is.
    Top-level storage_locations, serve, environments are merged into runtime.
    """
    if not metadata or not isinstance(metadata, dict):
        return {}
    out = dict(metadata)
    if "ownership" not in out or not isinstance(out.get("ownership"), dict):
        out["ownership"] = {}
    if "lineage" not in out or not isinstance(out.get("lineage"), dict):
        out["lineage"] = {}
    out["runtime"] = _get_runtime(out)
    # Remove root-level keys that are now under runtime (canonical shape has them only in runtime)
    for key in ("storage_locations", "serve", "environments"):
        if key in out:
            del out[key]
    return out


def relationship_view(metadata: dict[str, Any]) -> dict[str, Any]:
    """
    Extract ownership lists, lineage lists, domain, storage_locations, and environments for join-table storage.

    Reads from metadata["ownership"], metadata["lineage"], metadata["domain"], and metadata["runtime"].
    """
    view: dict[str, Any] = {}
    o = metadata.get("ownership")
    if isinstance(o, dict):
        for k in OWNERSHIP_KEYS:
            v = o.get(k)
            if isinstance(v, list):
                view[k] = v
    lng = metadata.get("lineage")
    if isinstance(lng, dict):
        for k in LINEAGE_KEYS:
            v = lng.get(k)
            if isinstance(v, list):
                view[k] = v
    domain = metadata.get("domain")
    if isinstance(domain, str):
        view["domain"] = domain
    runtime = metadata.get("runtime")
    if isinstance(runtime, dict):
        sl = runtime.get("storage_locations")
        if isinstance(sl, list):
            view["storage_locations"] = sl
        env = runtime.get("environments")
        if isinstance(env, list):
            view["environments"] = env
    # Backward compat: read from root if not in runtime
    if "storage_locations" not in view and isinstance(
        metadata.get("storage_locations"), list
    ):
        view["storage_locations"] = metadata["storage_locations"]
    if "environments" not in view and isinstance(metadata.get("environments"), list):
        view["environments"] = metadata["environments"]
    return view
