"""Web draft mapping. Public route activation waits for wire acceptance."""

from dataclasses import dataclass, field
from urllib.parse import urlsplit
from uuid import NAMESPACE_URL, uuid5


class CameraConfigError(ValueError):
    pass


def source_identity(value: str | int) -> str:
    if type(value) not in (str, int) or not str(value) or not str(value).strip():
        raise CameraConfigError("Invalid camera identity.")
    return str(value)


@dataclass(frozen=True)
class CameraStart:
    camera_id: str | int
    rtsp_url: str = field(repr=False)
    rec_camera_id: str | int | None = None
    config_version: str | int | None = None
    scenarios: tuple[tuple[str, bool], ...] | None = None
    start_wall_time_ms: int | None = None
    rtp_clock_rate: int | None = None

    def __post_init__(self):
        source_identity(self.camera_id)
        if self.rec_camera_id is not None:
            source_identity(self.rec_camera_id)
        if self.config_version is not None:
            source_identity(self.config_version)
        for value, minimum in ((self.start_wall_time_ms, 0), (self.rtp_clock_rate, 1)):
            if value is not None and (type(value) is not int or value < minimum):
                raise CameraConfigError("Invalid time anchor.")
        try:
            parsed = urlsplit(self.rtsp_url)
            valid = (parsed.scheme == "rtsp" and bool(parsed.hostname) and parsed.port != 0
                     and not any(c.isspace() or ord(c) < 32 for c in self.rtsp_url))
        except (TypeError, ValueError):
            valid = False
        if not valid:
            raise CameraConfigError("Invalid RTSP source.")
        if self.scenarios is not None:
            names = set()
            for name, enabled in self.scenarios:
                if type(name) is not str or type(enabled) is not bool or name in names:
                    raise CameraConfigError("Invalid scenario selection.")
                names.add(name)

    def runtime_spec(self, server_id: str) -> dict:
        source_id = source_identity(self.camera_id)
        # IDIMPL-01: 외부 식별 문자열을 보존하고 경로에 직접 사용하지 않는다.
        stream_id = "camera-" + uuid5(NAMESPACE_URL, source_id).hex
        return {
            "stream_id": stream_id, "source_id": source_id, "server_id": server_id,
            "site_id": None, "source_type": "rtsp", "uri": self.rtsp_url,
            "camera_id": self.camera_id, "rec_camera_id": self.rec_camera_id,
            "web_config_version": self.config_version,
            "time_anchor": {"start_wall_time_ms": self.start_wall_time_ms,
                            "rtp_clock_rate": self.rtp_clock_rate},
            "scenarios": "off", "store": False, "active_learning": False,
        }
