#!/usr/bin/env python3
# Generates docs/4plus1/diagrams/physical-view.svg per fireworks-tech-graph 4+1 baseline rules.
lines = []
W, H = 1280, 640
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
lines.append(f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" width="{W}" height="{H}" font-family="Segoe UI, Arial, sans-serif" font-size="13">')
lines.append('<title>Physical View (baseline, deployment-plane)</title>')
lines.append(f'<rect x="0" y="0" width="{W}" height="{H}" fill="#ffffff"/>')
lines.append(f'<rect x="0" y="0" width="{W}" height="60" fill="#f1f5f9"/>')
lines.append(f'<text x="40" y="36" font-size="20" font-weight="700" fill="#0f172a">Physical View — 部署平面与运行时边界 (baseline)</text>')
lines.append(f'<text x="40" y="52" font-size="11" fill="#64748b">单机 CLI 工具 · 2 部署平面 · 3 系统边界 · Node.js 18+ · 本地 FS 状态</text>')
lines.append('<defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#2563eb"/></marker>')
lines.append('<marker id="arrowGray" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#6b7280"/></marker></defs>')

# Plane 1: Developer Machine (E-022)
p1x, p1y, p1w, p1h = 40, 110, 820, 420
lines.append(f'<rect x="{p1x}" y="{p1y}" width="{p1w}" height="{p1h}" fill="#dbeafe" fill-opacity="0.12" stroke="#1e40af" stroke-width="2" stroke-dasharray="6 4" rx="14"/>')
lines.append(f'<text x="{p1x+18}" y="{p1y+28}" font-size="14" font-weight="700" fill="#1e40af">E-022 · Developer Machine</text>')
lines.append(f'<text x="{p1x+18}" y="{p1y+46}" font-size="10" fill="#475569">deployment-plane · Node.js 18+ · npm link / npx · 本地 FS 状态(.pipeline/ · gates/ · .claude/skills/)</text>')

# Plane 2: External Provider Runtimes (E-023)
p2x, p2y, p2w, p2h = 900, 200, 340, 220
lines.append(f'<rect x="{p2x}" y="{p2y}" width="{p2w}" height="{p2h}" fill="#fee2e2" fill-opacity="0.18" stroke="#991b1b" stroke-width="2" stroke-dasharray="6 4" rx="14"/>')
lines.append(f'<text x="{p2x+18}" y="{p2y+28}" font-size="14" font-weight="700" fill="#991b1b">E-023 · External Provider Runtimes</text>')
lines.append(f'<text x="{p2x+18}" y="{p2y+46}" font-size="10" fill="#7f1d1d">claude-code / codex CLI / gemini API</text>')

def module(cx, cy, eid, name, sub, fill="#ffffff", stroke="#2563eb"):
    w = 160; h = 50
    lines.append(f'<rect x="{cx-w//2}" y="{cy-h//2}" width="{w}" height="{h}" rx="8" fill="{fill}" stroke="{stroke}" stroke-width="1.6"/>')
    lines.append(f'<text x="{cx}" y="{cy-2}" text-anchor="middle" font-size="12" font-weight="600" fill="#0f172a">{name}</text>')
    lines.append(f'<text x="{cx}" y="{cy+12}" text-anchor="middle" font-size="9" fill="#64748b">{sub}</text>')
    lines.append(f'<text x="{cx-w//2+6}" y="{cy-h//2+12}" font-size="8" fill="#94a3b8">{eid}</text>')

module(150, 200, "E-012", "root", "CLI 入口")
module(330, 200, "E-004", "gate-engine", "门祡引擎")
module(510, 200, "E-009", "pipeline", "9-step runner")
module(150, 290, "E-002", "env", "ProcessGuardian/Port")
module(330, 290, "E-019", "telemetry", "事件上报")
module(150, 380, "E-018", "team", "Worker 协作")
module(330, 380, "E-014", "skills-mirror", "skill 镜像")
module(510, 380, "E-013", "shared", "mapping/openapi")

# Claude Code hook host annotation
lines.append(f'<rect x="80" y="80" width="180" height="24" fill="#fef3c7" stroke="#92400e" rx="4"/>')
lines.append(f'<text x="170" y="96" text-anchor="middle" font-size="11" font-weight="600" fill="#92400e">Claude Code (IDE/CLI host)</text>')

# external provider endpoint inside plane 2
lines.append(f'<rect x="{p2x+60}" y="{p2y+90}" width="220" height="50" rx="8" fill="#ffffff" stroke="#991b1b" stroke-width="1.6"/>')
lines.append(f'<text x="{p2x+170}" y="{p2y+108}" text-anchor="middle" font-size="12" font-weight="600" fill="#0f172a">providers adapter</text>')
lines.append(f'<text x="{p2x+170}" y="{p2y+124}" text-anchor="middle" font-size="9" fill="#64748b">E-010 · claudeCodeAdapter / traeAdapter / cursorAdapter</text>')

# CI annotation
lines.append(f'<rect x="940" y="460" width="300" height="60" fill="#f3e8ff" fill-opacity="0.5" stroke="#7c3aed" rx="6"/>')
lines.append(f'<text x="1090" y="480" text-anchor="middle" font-size="11" font-weight="700" fill="#5b21b6">CI (optional)</text>')
lines.append(f'<text x="1090" y="498" text-anchor="middle" font-size="10" fill="#5b21b6">npm run build / test / lint</text>')
lines.append(f'<text x="1090" y="512" text-anchor="middle" font-size="10" fill="#5b21b6">telemetry 可 flag 关闭</text>')

def edge(x1, y1, x2, y2, label="", color="#2563eb", dash=None):
    d = f' stroke-dasharray="{dash}"' if dash else ''
    m = "arrow" if color == "#2563eb" else "arrowGray"
    lines.append(f'<path d="M {x1},{y1} L {x2},{y2}" fill="none" stroke="{color}" stroke-width="1.6"{d} marker-end="url(#{m})"/>')
    if label:
        mx, my = (x1+x2)/2, (y1+y2)/2
        lines.append(f'<rect x="{mx-len(label)*3-2}" y="{my-7}" width="{len(label)*6+4}" height="14" fill="#ffffff" opacity="0.92"/>')
        lines.append(f'<text x="{mx}" y="{my+3}" text-anchor="middle" font-size="9" fill="#1e40af">{label}</text>')

edge(200, 225, 280, 265, "R-022 async", color="#6b7280", dash="4,2")  # root -> telemetry
edge(230, 290, p2x+60, p2y+115, "R-026 «calls» async", color="#6b7280", dash="4,2")  # env -> external providers

# boundary 2 hook arrow: Claude Code box -> root
edge(170, 104, 150, 175, "PreToolUse/PostToolUse", color="#2563eb")

# boundary labels
lines.append(f'<text x="700" y="510" font-size="10" font-weight="600" fill="#1e40af">boundary 1 · 本机文件系统</text>')
lines.append(f'<text x="700" y="522" font-size="9" fill="#475569">.pipeline/*.json · gates/*.yaml · .claude/skills/*</text>')
lines.append(f'<text x="900" y="170" font-size="10" font-weight="600" fill="#991b1b">boundary 3 · 外部 provider 进程/API</text>')
lines.append(f'<text x="80" y="170" font-size="10" font-weight="600" fill="#92400e">boundary 2 · Claude Code hook 协议</text>')

# R-025 inline note
lines.append(f'<text x="{p1x+18}" y="{p1y+62}" font-size="10" fill="#1e40af">R-025 «deploys» → E-012 root (npm bin entry)</text>')

# legend
lx, ly = 40, 552
lines.append(f'<rect x="{lx}" y="{ly}" width="{W-80}" height="38" fill="#f8fafc" stroke="#cbd5e1" rx="6"/>')
lines.append(f'<text x="{lx+10}" y="{ly+16}" font-size="11" font-weight="600" fill="#475569">deployment_impact:</text>')
lines.append(f'<text x="{lx+110}" y="{ly+16}" font-size="10" fill="#0f172a">node_runtime=Node.js 18+ | package_distribution=npm | state_persistence=本地 FS | external_processes=claude/codex/gemini</text>')
lines.append(f'<text x="{lx+10}" y="{ly+32}" font-size="10" fill="#64748b">RR-001: telemetry 默认开启，跨机器重装会丢失 telemetry-id；数据保护依赖文件系统权限 (severity: low)</text>')

lines.append(f'<rect x="0" y="{H-44}" width="{W}" height="44" fill="#f1f5f9" stroke="#cbd5e1"/>')
lines.append(f'<text x="40" y="{H-26}" font-size="11" fill="#0f172a">机器可校验源:physical-view.puml + harness-toolkit-baseline.yaml</text>')
lines.append(f'<text x="40" y="{H-10}" font-size="10" fill="#64748b">本图按 per-view 裁剪策略产出，不代表完整关系集(详见 ADR-0018)</text>')
lines.append('</svg>')

out = 'docs/4plus1/diagrams/physical-view.svg'
with open(out, 'w', encoding='utf-8') as f:
    f.write('\n'.join(lines))
print(f"WROTE {out} ({len(lines)} lines)")
