import os
from datetime import datetime, timezone

from .clients.spec_cl import SpectrometerClient
from .clients.reco_cl import ReconstructorClient


def _iso_now() -> str:
    return datetime.now(timezone.utc).isoformat()


# -- конфиг из env ---------------------------------------------------------
_SPECTROMETER_URL  = os.getenv("SPECTROMETER_URL",      "http://localhost:1818")
_RECONSTRUCTOR_URL = os.getenv("RECONSTRUCTOR_URL",     "http://localhost:8081")
_SPEC_USER         = os.getenv("SPECTROMETER_USER",     "admin")
_SPEC_PASS         = os.getenv("SPECTROMETER_PASSWORD", "admin")
_WAIT_TIMEOUT      = float(os.getenv("WAIT_DATA_TIMEOUT", "120"))

# -- ленивая инициализация клиентов ----------------------------------------
# Клиенты создаются при первом вызове шага, а не при импорте модуля.
# Это предотвращает падение оркестратора при старте, если спектрометр
# или реконструктор ещё не поднялись.

_spec: SpectrometerClient | None = None
_reco: ReconstructorClient | None = None


def _get_spec() -> SpectrometerClient:
    global _spec
    if _spec is None:
        _spec = SpectrometerClient(
            base_url=_SPECTROMETER_URL,
            username=_SPEC_USER,
            password=_SPEC_PASS,
        )
    return _spec


def _get_reco() -> ReconstructorClient:
    global _reco
    if _reco is None:
        _reco = ReconstructorClient(base_url=_RECONSTRUCTOR_URL)
    return _reco


# -- Шаг 1: старт измерения ------------------------------------------------

def start_measurement(info=None, **kwargs):
    """POST /api/measure/ → measurement_id (int)."""
    payload: dict = {}
    if info is not None:
        payload["info"] = dict(info)
    else:
        # поддержка старых шаблонов с плоскими полями
        payload["info"] = dict(kwargs)
    payload["info"].setdefault("time", _iso_now())
    meas_id = _get_spec().start_measurement(payload)
    return {"measurement_id": meas_id}


# -- Шаг 2: ждём готовность данных -----------------------------------------

def wait_data_ready(measurement_id=None, **kwargs):
    """Poll /api/measure/{id}/state/ до data_ready=true."""
    if measurement_id is None:
        raise ValueError(
            "wait_data_ready: measurement_id не пришёл из предыдущего шага"
        )
    state = _get_spec().wait_data_ready(measurement_id, timeout_s=_WAIT_TIMEOUT)
    return {"data_ready": state.get("data_ready", False), "state": state}


# -- Шаг 3: забираем данные ------------------------------------------------

def fetch_data(measurement_id=None, **kwargs):
    """GET /api/measure/{id}/data/ → нормализованный список записей."""
    if measurement_id is None:
        raise ValueError(
            "fetch_data: measurement_id не пришёл из предыдущего шага"
        )
    raw = _get_spec().get_data(measurement_id)

    # DRF с пагинацией → {"results": [...]}, без → список, единственная запись → dict
    if isinstance(raw, dict) and "results" in raw:
        data_list = raw["results"]
    elif isinstance(raw, list):
        data_list = raw
    else:
        data_list = [raw]

    return {"data": data_list, "measurement_id": measurement_id}


# -- Шаг 4: реконструкция --------------------------------------------------

def run_reconstruction(
    measurement_id=None,
    raw_file=None,
    sequence_name="linear",
    digit="2d",
    phase_shift=False,
    **kwargs,
):
    """
    POST /h5/reconstruct.

    file_raw_id берётся из явного raw_file (если задан шаблоном),
    иначе падает назад на measurement_id из контекста.
    Лишние ключи контекста (data, state, …) не передаются реконструктору.
    """
    body: dict = {
        "sequence_name": sequence_name,
        "digit": digit,
        "phase_shift": phase_shift,
    }
    if raw_file is not None:
        body["file_raw_id"] = raw_file
    elif measurement_id is not None:
        body["file_raw_id"] = str(measurement_id)

    resp = _get_reco().reconstruct(body, raw_format="h5")
    session_id = resp.get("session_id")
    session = _get_reco().get_session(session_id) if session_id else {}
    return {"session_id": session_id, "status": session.get("status", "unknown")}


# --------------------------------------------------------------------------

TASK_REGISTRY = {
    "start_measurement": start_measurement,
    "wait_data_ready":   wait_data_ready,
    "fetch_data":        fetch_data,
    "run_reconstruction": run_reconstruction,
}
