# -*- coding: utf-8 -*-
"""K1 트렌치 실측 시드 생성기 — CAD(전기,설비 트렌치.dwg → DXF)에서 홀1~5 트렌치 채널을
추출해 V61 마이그레이션 SQL을 산출한다(PLANNING R4 실측 대체).

실측 구조(레이어 5-트랜치): 홀당 가로 채널 7개(쌍선 폭 ~1.3m·피치 9m·로컬 y≈5.85 시작),
채널을 따라 6m 간격 공급 포인트를 생성한다(포인트 간격은 샘플링 — 채널 위치·연장은 실측).

사용:
  python tools/cad/extract_trench_seed.py <k1_trench.dxf> <out.sql>

좌표 규약: CAD 전건물 mm → 홀 로컬 m. 홀 바닥 X원점 293.5m·폭 171m, Y밴드 80.1m+63m 피치
(밴드 n ↔ 홀 n 가정 — docs/analysis/cad-extraction.md 한계 참조).
"""
import io
import math
import sys
from collections import defaultdict

import ezdxf

MM = 1000.0
FLOOR_X0, FLOOR_W = 293.5, 171.0
BAND_Y0, BAND_H = 80.1, 63.0
HALLS = [1, 2, 3, 4, 5]
LAYER_KEYS = ("트렌치", "트랜치")
MIN_RUN_M = 30.0
POINT_STEP_M = 6.0
ROW_SNAP_M = 1.5  # 쌍선(채널 양변) 병합 허용


def main():
    src, out_sql = sys.argv[1], sys.argv[2]
    doc = ezdxf.readfile(src)
    msp = doc.modelspace()

    # 홀별 → 채널 행(스냅된 로컬 y) → x구간 목록
    runs = {n: defaultdict(list) for n in HALLS}
    for e in msp:
        if not any(k in e.dxf.layer for k in LAYER_KEYS):
            continue
        pts = None
        if e.dxftype() == "LINE":
            pts = [(e.dxf.start[0] / MM, e.dxf.start[1] / MM), (e.dxf.end[0] / MM, e.dxf.end[1] / MM)]
        elif e.dxftype() == "LWPOLYLINE":
            p = [(q[0] / MM, q[1] / MM) for q in e.get_points()]
            if len(p) >= 2:
                pts = p
        if not pts:
            continue
        for a, b in zip(pts, pts[1:]):
            if math.hypot(b[0] - a[0], b[1] - a[1]) < MIN_RUN_M:
                continue
            if abs(b[1] - a[1]) > 0.5:  # 가로 채널만(세로 피더는 M4 후속)
                continue
            my = (a[1] + b[1]) / 2
            for n in HALLS:
                y0 = BAND_Y0 + (n - 1) * BAND_H
                if y0 <= my < y0 + BAND_H:
                    lx0 = max(0.0, min(a[0], b[0]) - FLOOR_X0)
                    lx1 = min(FLOOR_W, max(a[0], b[0]) - FLOOR_X0)
                    if lx1 - lx0 >= MIN_RUN_M:
                        # 홀 로컬 y=0은 도면 상단(DWG 언더레이 기준) — CAD y 상하 반전.
                        # (V61은 미반전 시드 — V62에서 63-y 보정 완료. 재생성 시 본 반전이 정본.)
                        runs[n][round(BAND_H - (my - y0), 1)].append((lx0, lx1))

    sql = io.StringIO()
    sql.write(
        "-- V61: K1 홀1~5 트렌치 실측 시드 — CAD(전기,설비 트렌치.dwg) 레이어 5-트랜치 추출.\n"
        "-- 생성기: tools/cad/extract_trench_seed.py (재생성 가능·결정적). 근거: docs/analysis/cad-extraction.md.\n"
        "-- 실측: 홀당 가로 채널(쌍선 병합 중심선)·연장 실좌표. 포인트 6m 간격은 샘플링 규약.\n"
        "-- 정책: 실측 확보 홀(H1~H5)의 가정(is_assumed) 포인트를 실측 포인트로 대체. K2(H6~10)는 가정 유지.\n"
        "-- 공급 플래그는 V5 정책 유지(공동구 — 전 서비스 true·gas는 홀 보유 여부), 위치만 실측 대체.\n\n"
    )
    total_rows = 0
    report = []
    for n in HALLS:
        # 쌍선 병합: 인접(<=ROW_SNAP_M) 행을 채널 하나로
        rows = sorted(runs[n].items())
        merged = []  # (center_y, x0, x1)
        for y, spans in rows:
            x0 = min(s[0] for s in spans)
            x1 = max(s[1] for s in spans)
            if merged and y - merged[-1][0] <= ROW_SNAP_M:
                py, px0, px1 = merged[-1]
                merged[-1] = ((py + y) / 2, min(px0, x0), max(px1, x1))
            else:
                merged.append((y, x0, x1))
        report.append(f"H{n}: 채널 {len(merged)}개 " + ", ".join(f"y={m[0]:.1f} x[{m[1]:.1f}..{m[2]:.1f}]" for m in merged))
        if not merged:
            continue
        total_rows += len(merged)
        sql.write(f"-- H{n}: 실측 채널 {len(merged)}개\n")
        sql.write(f"DELETE FROM trench WHERE hall_id = 'H{n}' AND is_assumed;\n")
        values = []
        for ri, (cy, x0, x1) in enumerate(merged, 1):
            k = 0
            x = x0
            while x <= x1 + 1e-6:
                k += 1
                values.append(
                    f"('H{n}-RT-{ri}-{k}', 'H{n}', ST_SetSRID(ST_MakePoint({x:.2f}, {cy:.2f}), 0), "
                    f"true, true, true, true, (SELECT has_gas FROM hall WHERE id='H{n}'), false)"
                )
                x += POINT_STEP_M
        sql.write(
            "INSERT INTO trench (id, hall_id, geom, supply_power, supply_water, supply_air, supply_network, supply_gas, is_assumed)\nVALUES\n    "
            + ",\n    ".join(values)
            + "\nON CONFLICT (id) DO UPDATE SET geom = EXCLUDED.geom, is_assumed = EXCLUDED.is_assumed;\n\n"
        )

    with open(out_sql, "w", encoding="utf-8") as f:
        f.write(sql.getvalue())
    for line in report:
        print(line)
    print(f"OK -> {out_sql} (채널 {total_rows}개)")


if __name__ == "__main__":
    main()
