from __future__ import annotations

from urllib.parse import urlparse

# v1 flat topic model note:
# - Topic identity normalization is a URI/view concern only.
# - It must stay independent from coordinator runtime state/ledger semantics.
# - Coordinator ownership resolution (address + epoch) lives in routing/control paths.

_FLOWNET_URI_SCHEME = "flownet://"
_TOPIC_PREFIX = "topic:"
_DEFAULT_CLUSTER = "local"
_DEFAULT_SCOPE = "default"
_TOPIC_KIND = "topic"


def canonical_topic_key(topic_id: str) -> str:
    raw_topic_id = _require_topic_id(topic_id)
    if raw_topic_id.startswith(_FLOWNET_URI_SCHEME):
        return _normalize_topic_uri(raw_topic_id)
    if raw_topic_id.startswith(_TOPIC_PREFIX):
        topic_name = raw_topic_id[len(_TOPIC_PREFIX):].strip()
        if not topic_name:
            raise ValueError("Topic shorthand must include a non-empty name.")
        return _build_topic_uri(topic_name)
    # Preserve non-topic namespaced channels (for example `service:<name>`)
    # so existing compatibility behavior remains unchanged in this phase.
    if ":" in raw_topic_id:
        return raw_topic_id
    return _build_topic_uri(raw_topic_id)


def _require_topic_id(topic_id: str) -> str:
    if not isinstance(topic_id, str):
        raise TypeError("topic_id must be a string.")
    value = topic_id.strip()
    if not value:
        raise ValueError("topic_id must not be empty.")
    return value


def _build_topic_uri(topic_name: str) -> str:
    return f"{_FLOWNET_URI_SCHEME}{_DEFAULT_CLUSTER}/{_DEFAULT_SCOPE}/{_TOPIC_KIND}/{topic_name}"


def _normalize_topic_uri(topic_uri: str) -> str:
    parsed = urlparse(topic_uri)
    if parsed.scheme != "flownet":
        raise ValueError(f"Unsupported topic URI scheme: {parsed.scheme!r}")
    if not parsed.netloc:
        raise ValueError("Topic URI must include a cluster segment.")
    path_segments = [segment for segment in parsed.path.split("/") if segment]
    if len(path_segments) < 3:
        raise ValueError(
            "Topic URI must follow flownet://<cluster>/<scope>/topic/<name>."
        )
    scope, kind, *name_segments = path_segments
    if kind != _TOPIC_KIND:
        return topic_uri
    topic_name = "/".join(name_segments).strip("/")
    if not topic_name:
        raise ValueError("Topic URI must include a non-empty topic name.")
    return f"{_FLOWNET_URI_SCHEME}{parsed.netloc}/{scope}/{_TOPIC_KIND}/{topic_name}"


__all__ = ["canonical_topic_key"]
