"""W19B-1: FloorPlanOverlayPayloadStep — deterministic per-bg overlay payload.

``floor_plan_prompt`` (v6) + ``background_master_plan`` checkpoint 만 consume.
LLM / image / VLM API call 0, DB / ImageAsset write 0.

Gates:
  - ``settings.background_mode`` ∉ {"on","floor_plan_anchored"} → not_applicable.
  - ``settings.floor_plan_prompt_version`` != "6" → not_applicable. default v5
    path 는 use/ignore arrays 가 v5 schema 에 없어 의미 없음. v5 callers
    그대로 회귀 0.

체크포인트 data shape:
  ``data.overlays`` = ``{bg_id: per-bg payload}``, build_overlay_payload 출력
  그대로. 실패 시 ``data.error`` 가 set 되고 ``failed_count=1``, ``data.overlays``
  는 빈 dict.
"""
from __future__ import annotations

import hashlib
import json
import logging
from pathlib import Path
from typing import Any, Dict, Optional

from app.core.step_runner import StepRunner
from app.modules.pipeline.floor_plan_overlay_payload import (
    FloorPlanOverlayPayloadError,
    build_overlay_payload,
)

logger = logging.getLogger(__name__)

SCHEMA_VERSION = 1
# Deterministic step — no prompt template. Stamp constant for config_hash
# stability across runs / resume.
PROMPT_VERSION = "1"


class FloorPlanOverlayPayloadStep(StepRunner):
    def _config_hash(self) -> str:
        from app.core.config import settings

        payload = {
            "background_mode": settings.background_mode,
            "floor_plan_prompt_version": settings.floor_plan_prompt_version,
            "schema_version": SCHEMA_VERSION,
            "prompt_version": PROMPT_VERSION,
        }
        return hashlib.sha256(
            json.dumps(payload, sort_keys=True).encode("utf-8")
        ).hexdigest()[:16]

    def _load_prev_checkpoint(
        self, step_id: str
    ) -> Optional[Dict[str, Any]]:
        from app.core.config import settings

        cp = (
            Path(settings.projects_dir)
            / self.project_id
            / "checkpoints"
            / "episodes"
            / self.episode_id
            / step_id
            / "manifest.json"
        )
        if cp.exists():
            try:
                return json.loads(cp.read_text(encoding="utf-8"))
            except Exception as exc:
                logger.warning(
                    "floor_plan_overlay_payload: %s parse failed: %s",
                    step_id,
                    exc,
                )
        return None

    def _not_applicable(self) -> Dict[str, Any]:
        return {
            "applicable_count": 0,
            "completed_count": 0,
            "failed_count": 0,
            "schema_version": SCHEMA_VERSION,
            "config_hash": self._config_hash(),
            "data": {},
        }

    def _execute(self, mode: str = "resume") -> Dict[str, Any]:
        from app.core.config import settings

        if settings.background_mode not in {"on", "floor_plan_anchored"}:
            return self._not_applicable()
        # v7 is schema-compatible with v6 (Rule 11 self-fidelity wording
        # only); accept both. default v5 path: overlay payload 는
        # use/ignore arrays 가 없어 산출 불가. fail-closed 대신
        # not_applicable — v5 caller 회귀 0.
        from app.core.fp_prompt_compat import V6_COMPATIBLE_FP_PROMPT_VERSIONS
        if settings.floor_plan_prompt_version not in V6_COMPATIBLE_FP_PROMPT_VERSIONS:
            return self._not_applicable()

        fp_prompt_cp = self._load_prev_checkpoint("floor_plan_prompt")
        master_plan_cp = self._load_prev_checkpoint("background_master_plan")
        fp_prompt_data = (fp_prompt_cp or {}).get("data") or {}
        master_plan_data = (master_plan_cp or {}).get("data") or {}

        try:
            overlays = build_overlay_payload(
                fp_prompt_data=fp_prompt_data,
                master_plan_data=master_plan_data,
            )
        except FloorPlanOverlayPayloadError as exc:
            logger.error("floor_plan_overlay_payload: %s", exc)
            return {
                "applicable_count": 1,
                "completed_count": 0,
                "failed_count": 1,
                "schema_version": SCHEMA_VERSION,
                "config_hash": self._config_hash(),
                "data": {"error": str(exc)[:200], "overlays": {}},
            }

        return {
            "applicable_count": 1,
            "completed_count": 1,
            "failed_count": 0,
            "schema_version": SCHEMA_VERSION,
            "config_hash": self._config_hash(),
            "data": {"overlays": overlays},
        }
