import json
import logging
import os
from datetime import datetime
from pathlib import Path

logger = logging.getLogger("MRISequenceInterpreter")


class PostRequestGenerator:
    """
    Генератор POST-запроса для аппаратного сервиса.

    Формирует JSON-манифест в формате, совместимом с тестовой машиной
    (см. POST_request_mes.txt), подставляя реальные имена выходных файлов
    интерпретатора. При наличии hw_service_url отправляет запрос асинхронно.
    """

    # ------------------------------------------------------------------
    # Public interface
    # ------------------------------------------------------------------

    def build(
        self,
        seq_data: dict,
        adc_values: list,
        sequence_path: str,
        output_dir: str,
        hw_cfg: dict,
        rf_raster_time: float,
    ) -> dict:
        """
        Строит словарь POST-запроса.

        Parameters
        ----------
        seq_data        : выходной словарь PulseqLoader (для определения наличия градиентов)
        adc_values      : длительности ADC-окон в секундах (из xml_generator)
        sequence_path   : путь к исходному .seq файлу
        output_dir      : директория с выходными артефактами
        hw_cfg          : содержимое hw_config.json
        rf_raster_time  : шаг RF-растра в секундах (из params["rf_raster_time"])
        """
        # Use the actual output directory of this interpretation run as the
        # file base.  file_path_prefix in hw_config is intentionally ignored
        # here — it was a legacy hardcoded path.  The orchestrator will remap
        # the container-internal output_dir to the Windows host path later via
        # _patch_seq_file_paths() before forwarding to the spectrometer.
        def _f(filename: str) -> str:
            return os.path.join(output_dir, filename)

        iadc_cfg = dict(hw_cfg.get("iadc", {}))
        srate = iadc_cfg.get("srate", 8_000_000)
        iadc_cfg["points"] = [int(v * srate) for v in adc_values]

        isync_cfg = dict(hw_cfg.get("isync", {}))
        isync_cfg["file"] = _f("sync_v2.xml")

        isdr_cfg = dict(hw_cfg.get("isdr", {}))
        isdr_cfg["file"] = _f(f"rf_{rf_raster_time}_raster.bin")

        igrax_cfg = dict(hw_cfg.get("igrax", {}))
        igray_cfg = dict(hw_cfg.get("igray", {}))
        igraz_cfg = dict(hw_cfg.get("igraz", {}))

        # Включаем ось, если в seq_data присутствует соответствующий сигнал
        igrax_cfg["file"] = _f("gx.txt")
        igray_cfg["file"] = _f("gy.txt")
        igraz_cfg["file"] = _f("gz.txt")

        if "gx" in seq_data:
            igrax_cfg["enabled"] = True
        if "gy" in seq_data:
            igray_cfg["enabled"] = True
        if "gz" in seq_data:
            igraz_cfg["enabled"] = True

        infostr = Path(sequence_path).stem

        payload = {
            "info": {
                "infostr": infostr,
                "engine": "DefaultEngine",
                "time": datetime.now().strftime("%Y-%m-%d %H:%M"),
                "iadc": iadc_cfg,
                "isync": isync_cfg,
                "isdr": isdr_cfg,
                "igrax": igrax_cfg,
                "igray": igray_cfg,
                "igraz": igraz_cfg,
            }
        }
        return payload

    def write(self, payload: dict, output_dir: str) -> str:
        """
        Сохраняет payload в <output_dir>/post_request.json.
        Возвращает абсолютный путь к файлу.
        """
        os.makedirs(output_dir, exist_ok=True)
        out_path = os.path.join(output_dir, "post_request.json")
        with open(out_path, "w", encoding="utf-8") as f:
            json.dump(payload, f, indent=4)
        logger.info("POST-запрос сохранён: %s", out_path)
        return out_path

    async def send(self, payload: dict, url: str) -> int:
        """
        Асинхронно отправляет payload как JSON POST на url.
        Возвращает HTTP-статус ответа (или 0 при ошибке соединения).
        Ошибки логируются, но не прерывают интерпретацию.
        """
        try:
            import httpx
            async with httpx.AsyncClient(timeout=10.0) as client:
                response = await client.post(url, json=payload)
            logger.info(
                "POST отправлен на %s - статус: %d", url, response.status_code
            )
            return response.status_code
        except ImportError:
            # Резервный вариант: синхронный requests через поток
            return await self._send_via_requests(payload, url)
        except Exception as exc:
            logger.warning("Не удалось отправить POST на %s: %s", url, exc)
            return 0

    # ------------------------------------------------------------------
    # Private helpers
    # ------------------------------------------------------------------

    @staticmethod
    async def _send_via_requests(payload: dict, url: str) -> int:
        import asyncio
        try:
            import requests as _requests

            def _do_post():
                resp = _requests.post(url, json=payload, timeout=10)
                return resp.status_code

            status = await asyncio.to_thread(_do_post)
            logger.info("POST отправлен на %s - статус: %d", url, status)
            return status
        except Exception as exc:
            logger.warning("Не удалось отправить POST на %s: %s", url, exc)
            return 0
