
    cUGj                    d    d Z ddlmZ ddlmZmZmZ g dZ	 	 	 	 d	dZ	 	 	 	 d
dZ		 	 	 	 	 	 ddZ
y)u  building_fp_link — W-G (2026-07-03): same-building indoor FP → outdoor 산출물 구조 참조.

같은 ``building_groups`` 그룹(=하나의 건물/부지)에 실내·실외 location 이 공존하면,
그 건물의 **실내 floor plan** 이 건물 크기·층수·개구부(문/창) 배치의 구조 SOT 다.
외부 산출물(aerial base / bg plate)이 이 fp 를 참조하지 않으면 건물 규모·구조를
근거 없이 상상해 축소/왜곡한다 — 이 모듈은 그 연결을 **구조 필드만으로** 조인한다.

데이터 소스 (전부 checkpoint 구조 필드 — 파일명/라벨/substring 파싱 금지):
  - ``background_classify.data.building_groups[].members[{loc_id, is_indoor,
    shot_count}]`` + ``anchor_loc``
  - ``background_master_plan.data.background_catalog[bg_id].{loc_id, depends_on_fp}``
    (fp_id↔loc 매핑의 SOT — floor_plan_render ``_compute_fp_primary_loc_ids`` 와
    동일한 depends_on_fp 역참조 조인)

순수 함수만 — DB/파일 접근 없음. 소비자(outdoor_site_layout / background_render)가
flag ``outdoor_building_fp_ref_enabled`` ON 일 때만 호출한다(OFF = 미호출 = 기존
경로 byte-identical).
    )annotations)AnyDictList)indoor_fp_ids_by_locbuild_outdoor_building_fp_linkbuild_group_membership_by_locc           	        i }| xs g D ]  }t        |t              s|j                  d      xs g D cg c]3  }t        |t              r!t        |j                  d      t              r|5 }}|sht	        |j                  d      xs d      }|st        d |D              }t        d |D              }|xr |}|D ]<  }t	        |j                  d      xs d      }	|	r|	|v r(|t        |d         |d||	<   >  |S c c}w )	ur  W-K (2026-07-03) — 모든 ``building_groups`` 멤버 loc 의 그룹 멤버십.

    반환: ``{loc_sid: {"group_id": str, "is_indoor": bool, "group_mixed": bool}}``.

    ``build_outdoor_building_fp_link`` 와 달리 **전 멤버**(실내 포함, mixed
    여부 무관)를 담는다 — same-place 렌더 체이닝의 그룹 anchor 는 등록
    (outdoor ok 렌더만)과 첨부(그룹 전 멤버) 판정에 실내 loc 멤버십이 필요하다.
    ``group_mixed`` 는 그룹에 실내·실외 멤버가 공존하는지(lane 재배열 정렬
    키). ``is_indoor`` 가 bool 이 아닌 멤버는 제외(구조 필드 계약 위반 —
    추측 금지). 같은 loc 이 여러 그룹에 등장하면 first-wins(그룹 순서 =
    classify 산출 순서, 결정론). 소비자(background_render)가 flag
    ``same_place_render_chain_enabled`` ON 일 때만 호출한다.
    members	is_indoorgroup_id c              3  &   K   | ]	  }|d      ywr   N .0ms     [/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/building_fp_link.py	<genexpr>z0build_group_membership_by_loc.<locals>.<genexpr>;   s     9A;s   c              3  (   K   | ]
  }|d       ywr   r   r   s     r   r   z0build_group_membership_by_loc.<locals>.<genexpr><   s     >gan,gs   loc_id)r   r   group_mixed)
isinstancedictgetboolstrany)
building_groupsoutgroupr   r   r   
has_indoorhas_outdoormixedlocs
             r   r	   r	      s     &(C &B&%&		),22
2!!T"z!%%2Dd'K 2 	 
 uyy,23999
>g>>*{AaeeHo+,C#*$!!K.1$CH	  '0 J+
s   8Dc                   i }| xs i j                         D ]  }t        |t              s|j                  d      }t        |t              r|s8|j                  d      xs g D ]?  }t        |t              s|s|j                  |t                     j                  |       A  |j                         D ci c]  \  }}|t        |       c}}S c c}}w )u?  ``background_catalog`` → ``{loc_sid: sorted [fp_id, ...]}``.

    catalog 의 각 bg entry 가 가진 ``loc_id`` + ``depends_on_fp`` 구조 필드만
    사용한다. loc 하나에 fp 가 여러 개면 정렬 리스트(결정론) — 선택은
    ``build_outdoor_building_fp_link`` 가 첫 번째를 취한다.
    r   depends_on_fp)
valuesr   r   r   r   
setdefaultsetadditemssorted)background_catalogr!   entryr&   fpfpss         r   r   r   J   s     C$*224%&ii!#s#3))O,22B"c"rsCE*..r2 3 5 .1YY[9[cC[999s   ;Cc                `   i }| xs g D ]  }t        |t              s|j                  d      xs g D cg c]  }t        |t              s| }}|D cg c]  }|j                  d      du s| }}|D cg c]  }|j                  d      du s| }}|r|s|D cg c]1  }|j                  t        |j                  d      xs d            r|3 }}|st        |j                  d      xs d      t	        fd|D        d	      }	|	t        |d
       d   }	t        |	j                  d            }
||
   d   }t        |j                  d      xs d      }|D ]0  }t        |j                  d      xs d      }|r||v r(||
|d||<   2  |S c c}w c c}w c c}w c c}w )ug  실내·실외 공존(building) 그룹의 outdoor loc → 대표 indoor fp 링크.

    반환: ``{outdoor_loc_sid: {"fp_id", "indoor_loc_sid", "group_id"}}``.

    규칙(전부 결정론):
      - 같은 그룹에 ``is_indoor`` True/False 멤버가 **공존**할 때만 링크를 만든다
        (순수 실외/실내 그룹 = 대상 아님).
      - indoor 대표 loc 선택 — **fp 를 실제 보유한** indoor 멤버 중에서:
          1. 그룹 ``anchor_loc`` 이 그 후보면 최우선 (classify 가 이미 '가장 많은
             실내 샷을 가진 주 공간'으로 뽑은 구조 필드),
          2. 아니면 ``shot_count`` 내림차순 → ``loc_id`` 오름차순.
      - fp 선택 — 대표 loc 의 정렬된 fp 리스트 첫 번째 (주 fp 1장).
      - 그룹 내 모든 outdoor 멤버가 같은 링크를 공유한다(같은 건물).
    r   r   TFr   r   
anchor_locc              3  ^   K   | ]$  }t        |j                  d             k(  s!| & yw)r   N)r   r   )r   r   anchors     r   r   z1build_outdoor_building_fp_link.<locals>.<genexpr>   s&     E
1c!%%/&:f&DQ
s   "--Nc                |    t        | j                  d      xs d       t        | j                  d      xs d      fS )N
shot_countr   r   r   )intr   r   )r   s    r   <lambda>z0build_outdoor_building_fp_link.<locals>.<lambda>   s5    |,122h-2.    )keyr   r   )fp_idindoor_loc_sidr   )r   r   r   r   nextr.   )r    fp_ids_by_loclinkr"   r   r   indooroutdoor
candidateschosen
indoor_locr=   r   out_locr6   s                 @r   r   r   `   s   $ ')D &B&%&$yy39r9R9jD>Q19R$CWk(:d(B!WC%Eg{);u)D1gEW 
!  QUU8_%:!;< v 	 
 UYY|,23E
Et
 > F H-.
j)!,uyy,23A!%%//R0Ggo ",$DM A 'V KQ SCE
s)   FFF!/F!9F&F&!6F+N)r    List[Dict[str, Any]]returnzDict[str, Dict[str, Any]])r/   zDict[str, Any]rI   Dict[str, List[str]])r    rH   r@   rJ   rI   zDict[str, Dict[str, str]])__doc__
__future__r   typingr   r   r   __all__r	   r   r   r   r;   r   <module>rO      sf   $ # " "))))X:&::,>)>'> >r;   