from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

import click
import typer

from remotivelabs.broker.auth import ApiKeyAuth, AuthMethod, NoAuth, TokenAuth
from remotivelabs.broker.signal import SignalValue
from remotivelabs.cli.broker.lib.broker import SubscribableSignal
from remotivelabs.cli.settings import settings

BrokerUrlOption = typer.Option("http://localhost:50051", is_eager=False, help="Broker URL", envvar="REMOTIVE_BROKER_URL")
ApiKeyOption = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY")


def get_auth(url: str, api_key: str | None) -> AuthMethod:
    if api_key:
        return ApiKeyAuth(api_key)
    if url.startswith("https"):
        token = settings.get_active_token()
        return TokenAuth(token) if token else NoAuth()
    return NoAuth()


@dataclass
class SignalSpec:
    namespace: str
    name: str
    value: list[SignalValue]


def _parse_scalar(raw: str) -> SignalValue:
    """Infer type of a single value string (int > float > bytes > str)."""
    try:
        return int(raw)
    except ValueError:
        pass
    try:
        return float(raw)
    except ValueError:
        pass
    try:
        return bytes.fromhex(raw)
    except ValueError:
        pass
    return raw


class _SignalSpecType(click.ParamType):
    name = "namespace:signal_name:v1[,v2,...]"

    def convert(self, value: str | SignalSpec, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> SignalSpec:
        if isinstance(value, SignalSpec):
            return value
        parts = value.split(":", 2)
        if len(parts) != 3:
            self.fail(f"must be in format namespace:signal_name:value, got: {value}", param, ctx)
        ns, name, raw = parts
        return SignalSpec(ns, name, [_parse_scalar(v) for v in raw.split(",")])


SignalSpecType = _SignalSpecType()


@dataclass
class FrameSpec:
    namespace: str
    name: str


class _FrameSpecType(click.ParamType):
    name = "namespace:frame_name"

    def convert(self, value: str | FrameSpec, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> FrameSpec:
        if isinstance(value, FrameSpec):
            return value
        parts = value.split(":", 1)
        if len(parts) != 2:
            self.fail(f"must be in format namespace:frame_name, got: {value}", param, ctx)
        return FrameSpec(namespace=parts[0], name=parts[1])


FrameSpecType = _FrameSpecType()


class _SubscribableSignalType(click.ParamType):
    name = "namespace:signal_name"

    def convert(
        self, value: str | SubscribableSignal, param: Optional[click.Parameter], ctx: Optional[click.Context]
    ) -> SubscribableSignal:
        if isinstance(value, SubscribableSignal):
            return value
        parts = value.split(":")
        if len(parts) != 2:
            self.fail(f"must be in format namespace:signal_name, got: {value}", param, ctx)
        return SubscribableSignal(namespace=parts[0], name=parts[1])


SubscribableSignalType = _SubscribableSignalType()
