"""Subject naming convention.

Default scheme: ``<prefix>.<agent>.<verb>.<topic>`` (with ``topic`` optional).

The prefix is configurable per fleet — ``cc.fleet`` is the convention used by
the FleetAgentGroup that this package was extracted from, but any dot-separated
prefix works. Examples:

- ``cc.fleet.miraku-home.query.status``
- ``cc.fleet.miraku-home.reply.status``
- ``cc.fleet.miraku-home.ack.status``
- ``cc.fleet.miraku-home.discovery``
- ``cc.fleet.miraku-home.policy``

Wildcards (``>``, ``*``) are NATS-native; this module exposes helpers that
produce them in idiomatic forms (``all_under_prefix()``, ``all_for_agent()``).
"""
from __future__ import annotations

from dataclasses import dataclass
from typing import NamedTuple


class ParsedSubject(NamedTuple):
    prefix: str | None
    agent: str | None
    verb: str | None
    topic: str | None

    @property
    def is_well_formed(self) -> bool:
        return self.prefix is not None and self.agent is not None and self.verb is not None


@dataclass(frozen=True)
class SubjectScheme:
    """Builder + parser for ``<prefix>.<agent>.<verb>.<topic>`` subjects."""

    prefix: str = "cc.fleet"

    def _join(self, *parts: str) -> str:
        return ".".join(p for p in parts if p)

    def agent(self, agent_id: str) -> str:
        return self._join(self.prefix, agent_id)

    def verb(self, agent_id: str, verb: str, topic: str | None = None) -> str:
        if topic:
            return self._join(self.prefix, agent_id, verb, topic)
        return self._join(self.prefix, agent_id, verb)

    def query(self, agent_id: str, topic: str) -> str:
        return self.verb(agent_id, "query", topic)

    def reply(self, agent_id: str, topic: str) -> str:
        return self.verb(agent_id, "reply", topic)

    def ack(self, agent_id: str, topic: str) -> str:
        return self.verb(agent_id, "ack", topic)

    def discovery(self, agent_id: str) -> str:
        return self.verb(agent_id, "discovery")

    def policy(self, agent_id: str) -> str:
        return self.verb(agent_id, "policy")

    def all_under_prefix(self) -> str:
        return f"{self.prefix}.>"

    def all_for_agent(self, agent_id: str) -> str:
        return f"{self.prefix}.{agent_id}.>"

    def parse(self, subject: str) -> ParsedSubject:
        prefix_parts = self.prefix.split(".")
        parts = subject.split(".")
        if len(parts) < len(prefix_parts) or parts[: len(prefix_parts)] != prefix_parts:
            return ParsedSubject(None, None, None, None)
        rest = parts[len(prefix_parts) :]
        agent = rest[0] if len(rest) > 0 else None
        verb = rest[1] if len(rest) > 1 else None
        topic = ".".join(rest[2:]) if len(rest) > 2 else None
        return ParsedSubject(self.prefix, agent, verb, topic)


DEFAULT_SCHEME = SubjectScheme()
