# -*- coding: utf-8 -*-
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "docs" / "assets" / "security_plan" / "generated"
OUT.mkdir(parents=True, exist_ok=True)

FONT_CANDIDATES = [
    Path("C:/Windows/Fonts/NotoSansKR-VF.ttf"),
    Path("C:/Windows/Fonts/malgun.ttf"),
]
BOLD_CANDIDATES = [
    Path("C:/Windows/Fonts/NotoSansKR-VF.ttf"),
    Path("C:/Windows/Fonts/malgunbd.ttf"),
]


def first_existing(paths):
    for path in paths:
        if path.exists():
            return path
    raise FileNotFoundError("No Korean font found")


FONT_PATH = first_existing(FONT_CANDIDATES)
BOLD_PATH = first_existing(BOLD_CANDIDATES)

FONT = ImageFont.truetype(str(FONT_PATH), 22)
SMALL = ImageFont.truetype(str(FONT_PATH), 18)
TINY = ImageFont.truetype(str(FONT_PATH), 15)
BOLD = ImageFont.truetype(str(BOLD_PATH), 25)
TITLE_FONT = ImageFont.truetype(str(BOLD_PATH), 32)


def wrap(draw, text, font, width):
    words = text.split(" ")
    lines = []
    current = ""
    for word in words:
        test = f"{current} {word}".strip()
        if draw.textbbox((0, 0), test, font=font)[2] <= width or not current:
            current = test
        else:
            lines.append(current)
            current = word
    if current:
        lines.append(current)
    return lines


def draw_title(draw, text):
    draw.text((40, 28), text, font=TITLE_FONT, fill="#0f172a")
    draw.line((40, 72, 1160, 72), fill="#cbd5e1", width=2)


def draw_box(draw, xy, title, lines, fill, outline="#334155"):
    x1, y1, x2, y2 = xy
    draw.rounded_rectangle(xy, radius=14, fill=fill, outline=outline, width=3)
    draw.text((x1 + 18, y1 + 14), title, font=BOLD, fill="#0f172a")
    y = y1 + 52
    for line in lines:
        for wrapped in wrap(draw, line, SMALL, x2 - x1 - 36):
            draw.text((x1 + 18, y), wrapped, font=SMALL, fill="#1f2937")
            y += 25


def draw_arrow(draw, p1, p2, color="#0f766e", width=5):
    draw.line([p1, p2], fill=color, width=width)
    x1, y1 = p1
    x2, y2 = p2
    if abs(x2 - x1) >= abs(y2 - y1):
        points = [(x2, y2), (x2 - 16, y2 - 9), (x2 - 16, y2 + 9)] if x2 >= x1 else [
            (x2, y2), (x2 + 16, y2 - 9), (x2 + 16, y2 + 9)
        ]
    else:
        points = [(x2, y2), (x2 - 9, y2 - 16), (x2 + 9, y2 - 16)] if y2 >= y1 else [
            (x2, y2), (x2 - 9, y2 + 16), (x2 + 9, y2 + 16)
        ]
    draw.polygon(points, fill=color)


def save_target_architecture():
    img = Image.new("RGB", (1200, 720), "#f8fafc")
    draw = ImageDraw.Draw(img)
    draw_title(draw, "보안대책서 보완용 목표 구성도")

    draw_box(draw, (55, 130, 275, 300), "전산실 센서", ["SHT30 온습도 센서", "STM32 보드", "측정 주기 5분"], "#e0f2fe")
    draw_box(draw, (360, 115, 610, 315), "STM32 단말 보안", ["기본 계정 변경", "환경파일 API 키", "로컬 로그", "불필요 서비스 차단"], "#dcfce7")
    draw_box(draw, (705, 95, 950, 330), "Cafe24 서버", ["PHP API", "MySQL DB", "임계 판정", "대시보드/보고서"], "#fef3c7")
    draw_box(draw, (990, 150, 1160, 280), "담당자", ["SMS 수신", "대시보드 확인", "현장 점검"], "#ede9fe")
    draw_box(draw, (405, 410, 905, 610), "보안 경계 및 통제", ["기관 내부 업무망과 분리", "HTTPS/TLS 전송", "기관 방화벽 목적지/포트 제한", "서버 관리자 페이지 인증/세션 보호"], "#ffe4e6")

    draw_arrow(draw, (275, 215), (360, 215))
    draw_arrow(draw, (610, 215), (705, 215))
    draw_arrow(draw, (950, 210), (990, 210))
    draw_arrow(draw, (610, 340), (705, 450))
    draw_arrow(draw, (405, 450), (250, 300))
    draw_arrow(draw, (905, 450), (1065, 285))
    draw.text((292, 184), "I2C/측정", font=TINY, fill="#334155")
    draw.text((626, 184), "HTTPS API", font=TINY, fill="#334155")
    draw.text((958, 180), "SMS/웹", font=TINY, fill="#334155")

    img.save(OUT / "security_plan_target_architecture.png")


def save_data_flow():
    img = Image.new("RGB", (1200, 720), "#ffffff")
    draw = ImageDraw.Draw(img)
    draw_title(draw, "데이터 흐름 및 보호조치 순서도")

    items = [
        ("1. 온습도 측정", ["SHT30 측정", "온도/습도 5분 주기"]),
        ("2. 단말 보고", ["측정값 수집", "periodic/startup", "정상 보고"]),
        ("3. API 전송", ["HTTPS/TLS", "HMAC/서명 검증", "API 키 일치"]),
        ("4. 서버 판정/저장", ["임계 판정(METRIC_*)", "sensor_metric", "sensor_log", "sms_log"]),
        ("5. 알림/대응", ["임계 SMS 발송", "정상복귀 SMS", "대시보드 확인"]),
    ]
    xs = [45, 275, 505, 735, 965]
    colors = ["#e0f2fe", "#dcfce7", "#fef3c7", "#ede9fe", "#ffe4e6"]
    for index, (head, body) in enumerate(items):
        draw_box(draw, (xs[index], 150, xs[index] + 190, 380), head, body, colors[index])
        if index < 4:
            draw_arrow(draw, (xs[index] + 190, 265), (xs[index + 1], 265), "#2563eb")

    draw_box(
        draw,
        (160, 470, 1040, 620),
        "문서에 추가할 보호대책 명시",
        ["전송 중 암호화, API 서명, 비밀값 분리, 최소 수집, 보관기간, 접근권한, 로그 위변조 방지, 백업/복구 점검을 표로 명시"],
        "#f1f5f9",
    )
    for x in [140, 370, 600, 830, 1060]:
        draw_arrow(draw, (x, 380), (x, 470), "#64748b", 3)

    img.save(OUT / "security_plan_data_flow.png")


def save_document_update_map():
    img = Image.new("RGB", (1200, 760), "#ffffff")
    draw = ImageDraw.Draw(img)
    draw_title(draw, "보안대책서 수정·추가 반영 위치")

    draw_box(draw, (60, 125, 340, 300), "1. 사업개요 수정", ["서버실 온습도(SHT30) 모니터링 범위", "임계 SMS/대시보드 운영 기능 반영"], "#e0f2fe")
    draw_box(draw, (460, 125, 740, 300), "2. 구성도 교체/보강", ["SHT30, STM32, Cafe24, MySQL, SMS", "업무망 분리와 HTTPS 경계 표시"], "#dcfce7")
    draw_box(draw, (860, 125, 1140, 300), "3. 기술대책 구체화", ["API 서명, 키 관리, 임계 판정", "로그/백업/보관기간"], "#fef3c7")
    draw_box(draw, (260, 430, 540, 605), "4. 운영 절차 추가", ["설치 점검 화면", "월간 보고서", "임계 경보/오프라인 대응 절차"], "#ede9fe")
    draw_box(draw, (660, 430, 940, 605), "5. 증적 목록 추가", ["점검 스크린샷", "SMS 테스트 결과", "DB 마이그레이션 적용 확인"], "#ffe4e6")
    draw_arrow(draw, (340, 215), (460, 215))
    draw_arrow(draw, (740, 215), (860, 215))
    draw_arrow(draw, (600, 300), (400, 430))
    draw_arrow(draw, (600, 300), (800, 430))

    img.save(OUT / "security_plan_document_update_map.png")


def main():
    save_target_architecture()
    save_data_flow()
    save_document_update_map()
    print(f"Generated images in {OUT}")


if __name__ == "__main__":
    main()
