# -*- coding: utf-8 -*-
"""브리핑 메일: 초안(.eml) 생성 + (선택) smtplib 발송.

기본 방식은 '메일 초안 파일 생성'이다 (2026-08-25 결정) — 프로그램은 받는사람·제목·
본문·사진이 채워진 .eml을 briefs 폴더에 만들 뿐, 스스로 전송하지 않는다. 발송은
사람이 자기 메일 프로그램에서 [보내기]로 한다. 외부 전송 기본값 OFF(PRINCIPLE 1)가
구조적으로 보장되고, SMTP 서버 설정도 필요 없다.

SMTP 자동 발송은 사내 메일서버가 있는 회사용 선택지로 유지한다. 산출물 파일 자체는
어떤 경우에도 전송하지 않는다 (PRINCIPLE 1).
"""

from __future__ import annotations

import base64
import re
import smtplib
import socket
from email.message import EmailMessage
from pathlib import Path

# 브리핑 HTML 안의 내장 이미지: src="data:image/png;base64,..." 꼴
_DATA_URI = re.compile(r'src="data:(image/[a-z+.-]+);base64,([A-Za-z0-9+/=]+)"')


def build_message(mail_cfg: dict, subject: str, html_body: str) -> EmailMessage:
    msg = EmailMessage()
    msg["Subject"] = subject
    msg["From"] = mail_cfg["sender"]
    msg["To"] = ", ".join(mail_cfg["recipients"])
    msg.set_content("HTML 메일을 지원하는 클라이언트에서 확인해주세요.")
    msg.add_alternative(html_body, subtype="html")
    return msg


def _html_with_cid_images(html_body: str) -> tuple[str, list[tuple[str, str, bytes]]]:
    """data: URI 내장 이미지를 cid: 참조로 바꾼다.

    Outlook 등 상당수 메일 클라이언트가 data: URI 이미지를 차단하므로,
    메일에서는 이미지를 별도 MIME 파트(Content-ID)로 실어야 보인다.
    돌려주는 값: (치환된 HTML, [(cid, mime, bytes)])"""
    images: list[tuple[str, str, bytes]] = []

    def _swap(m: re.Match) -> str:
        cid = f"img{len(images) + 1}"
        images.append((cid, m.group(1), base64.b64decode(m.group(2))))
        return f'src="cid:{cid}"'

    return _DATA_URI.sub(_swap, html_body), images


def write_eml(mail_cfg: dict, subject: str, html_body: str, out_path: Path) -> Path:
    """보내기만 하면 되는 메일 초안(.eml)을 만든다.

    X-Unsent: 1 헤더 덕에 Outlook에서 더블클릭하면 '받은 메일'이 아니라
    받는사람·제목·본문이 채워진 **작성 중인 초안**으로 열린다."""
    msg = EmailMessage()
    msg["Subject"] = subject
    msg["X-Unsent"] = "1"
    if mail_cfg.get("recipients"):
        msg["To"] = ", ".join(mail_cfg["recipients"])
    if mail_cfg.get("sender"):
        msg["From"] = mail_cfg["sender"]
    msg.set_content("HTML 메일을 지원하는 클라이언트에서 확인해주세요.")
    html, images = _html_with_cid_images(html_body)
    msg.add_alternative(html, subtype="html")
    payload = msg.get_payload()[-1]  # 방금 넣은 HTML 파트
    for cid, mime, data in images:
        maintype, subtype = mime.split("/", 1)
        payload.add_related(data, maintype=maintype, subtype=subtype, cid=f"<{cid}>")
    out_path.write_bytes(msg.as_bytes())
    return out_path


def _friendly_smtp_error(e: Exception) -> str:
    """SMTP 예외 → 어느 칸을 고쳐야 하는지 알려주는 한국어 메시지 (2026-08-25 UX 검토)."""
    if isinstance(e, socket.gaierror):
        return "SMTP 서버 주소를 찾을 수 없습니다 — 서버 주소 칸의 오타를 확인하세요."
    if isinstance(e, (TimeoutError, socket.timeout)):
        return ("서버가 응답하지 않습니다 — 포트 번호와 방식(STARTTLS/SSL)을 확인하세요. "
                "그룹웨어(하이웍스·네이버웍스 등)는 대부분 465 + SSL입니다.")
    if isinstance(e, smtplib.SMTPAuthenticationError):
        return ("로그인 거부 — 계정/비밀번호를 확인하세요. 그룹웨어는 로그인 비밀번호가 "
                "아니라 '앱 비밀번호' 발급이 필요할 수 있습니다.")
    if isinstance(e, smtplib.SMTPRecipientsRefused):
        return "받는 사람 주소가 거부되었습니다 — 주소 오타를 확인하세요."
    if isinstance(e, ConnectionRefusedError):
        return "서버가 연결을 거부했습니다 — 포트 번호를 확인하세요."
    if isinstance(e, smtplib.SMTPException):
        return f"메일 서버 오류: {e}"
    return str(e)


def send_brief(mail_cfg: dict, subject: str, html_body: str) -> bool:
    """발송했으면 True. enabled가 아니거나 수신자가 없으면 False (오류 아님).
    설정이 불완전하면 KeyError 대신 이유가 담긴 ValueError를 낸다."""
    if not mail_cfg.get("enabled"):
        return False
    if not mail_cfg.get("recipients"):
        return False
    missing = [k for k in ("smtp_host", "sender") if not mail_cfg.get(k)]
    if missing:
        raise ValueError(f"설정 누락: {', '.join(missing)} — 화면의 SMTP 서버/보내는 주소 칸을 채워주세요")
    if mail_cfg.get("username") and not mail_cfg.get("password"):
        raise ValueError("로그인 계정은 있는데 비밀번호가 없습니다 — 비밀번호 칸을 채워주세요")
    msg = build_message(mail_cfg, subject, html_body)
    host = mail_cfg["smtp_host"]
    port = int(mail_cfg.get("smtp_port", 25))
    try:
        # 그룹웨어 표준인 암시적 SSL(465) 지원 (2026-08-25 UX 검토)
        smtp_cls = smtplib.SMTP_SSL if mail_cfg.get("ssl") else smtplib.SMTP
        with smtp_cls(host, port, timeout=10) as smtp:
            if mail_cfg.get("starttls") and not mail_cfg.get("ssl"):
                smtp.starttls()
            if mail_cfg.get("username"):
                smtp.login(mail_cfg["username"], mail_cfg["password"])
            smtp.send_message(msg)
    except Exception as e:
        raise RuntimeError(_friendly_smtp_error(e)) from e
    return True
