"""Host-owned binding from provider model values to native call arguments."""
from __future__ import annotations

from dataclasses import dataclass
from typing import Literal, Protocol

from okstra_ctl.domain.provider import HostModelBinding
from okstra_ctl.domain.worker_exec import WorkerWriteCapability


class HostModelBindingError(ValueError):
    """Raised when a host has no explicit native model mapping."""


@dataclass(frozen=True)
class HostModelBindingRequest:
    host_runtime: str
    provider: str
    model_ref: str
    version_kind: Literal["pinned", "channel"]
    model_execution_value: str
    runner: str
    resolved_execution_value: str | None = None
    worker_write_capability: WorkerWriteCapability | None = None

    @property
    def execution_value(self) -> str:
        return self.resolved_execution_value or self.model_execution_value


class HostModelBindingPort(Protocol):
    def resolve(self, request: HostModelBindingRequest) -> HostModelBinding: ...


def _cli_binding(request: HostModelBindingRequest) -> HostModelBinding:
    return HostModelBinding(
        runner=request.runner,
        catalog_execution_value=request.model_execution_value,
        resolved_execution_value=request.execution_value,
        host_model_value=None,
        binding_fidelity="exact",
        worker_write_capability=request.worker_write_capability,
    )


class FailClosedHostModelBindingPort:
    def __init__(self, host_runtime: str) -> None:
        self._host_runtime = host_runtime

    def resolve(self, request: HostModelBindingRequest) -> HostModelBinding:
        if request.runner == "cli-wrapper":
            return _cli_binding(request)
        raise HostModelBindingError(
            "unsupported host model: "
            f"host={self._host_runtime!r}, provider={request.provider!r}, "
            f"model={request.execution_value!r}"
        )


class NativeExecutionValueHostModelBindingPort:
    """Binding for hosts whose native API accepts provider execution values."""

    def __init__(self, host_runtime: str, native_provider: str) -> None:
        self._host_runtime = host_runtime
        self._native_provider = native_provider

    def resolve(self, request: HostModelBindingRequest) -> HostModelBinding:
        if request.runner == "cli-wrapper":
            return _cli_binding(request)
        if not self._supports_native(request):
            raise HostModelBindingError(
                "unsupported host model: "
                f"host={request.host_runtime!r}, provider={request.provider!r}, "
                f"model={request.execution_value!r}"
            )
        return HostModelBinding(
            runner=request.runner,
            catalog_execution_value=request.model_execution_value,
            resolved_execution_value=request.execution_value,
            host_model_value=request.execution_value,
            binding_fidelity="exact",
            worker_write_capability=request.worker_write_capability,
        )

    def _supports_native(self, request: HostModelBindingRequest) -> bool:
        return (
            request.runner == "native-session"
            and request.host_runtime == self._host_runtime
            and request.provider == self._native_provider
            and bool(request.execution_value.strip())
        )
