#!/usr/bin/env python3
"""
PDF 워터마크 자동 생성기
- 모든 페이지: 사선 텍스트 워터마크 (희미, 겹치지 않게)
- 법률 고지: 기존 본문 위에 덮지 않고 **새 페이지**로 삽입
  · 표지(1p) 다음 = 2페이지
  · 문서 맨 끝 = 마지막 페이지

사용:
  .venv/bin/python watermark_pdf.py input.pdf -o output.pdf
  .venv/bin/python watermark_pdf.py input.pdf --text "BSD CLASS LICENSE" --buyer "홍길동" --order "ORD-1"
"""

from __future__ import annotations

import argparse
import sys
from datetime import date
from pathlib import Path

import fitz  # PyMuPDF


DEFAULT_MARK = "BSD CLASS LICENSE"

LEGAL_KO = """\
【법률 고지 · BSD CLASS LICENSE】

본 PDF 전자책 및 포함 콘텐츠는 저작권법 및 관련 법령의 보호를 받습니다.

라이선스 표기: {mark}
구매자/수신자: {buyer}
주문·식별번호: {order}
발급일: {issued}

■ 허용
  · 정당한 구매자(또는 정당한 수신자) 본인의 열람·학습 목적 사용

■ 금지
  · 무단 복제, 전송, 공유, 업로드(메신저·클라우드·웹하드·토렌트 등)
  · 유·무료 재판매, 재배포, 2차 가공 판매
  · 워터마크·본 고지 문구의 삭제·변조
  · 라이선스 코드·파일의 제3자 양도·공유

■ 위반 시
  저작권 침해에 따른 민사상 손해배상 청구 및 형사 처벌(벌금 또는 징역)의
  대상이 될 수 있습니다. 본 고지는 일반 안내이며 개별 사안에 대한
  법률 자문이 아닙니다.

■ 면책
  본문에 기재된 수익·성과·수치는 보장이나 투자 권유가 아닌 참고 예시일 수
  있습니다. 중요 결정은 해당 분야 전문가와 상의하십시오.

© {year} BSD CLASS LICENSE · All Rights Reserved
본 페이지는 자동 생성기에 의해 삽입된 전용 법률 페이지입니다.
"""

LEGAL_EN = """\
[LEGAL NOTICE · BSD CLASS LICENSE]

This PDF and its contents are protected by copyright and applicable laws.

License mark: {mark}
Licensee: {buyer}
Order / ID: {order}
Issued: {issued}

Permitted: personal reading/study by the legitimate licensee only.

Prohibited: copying, redistribution, resale, upload, removal of watermarks
or this notice, and sharing license credentials.

Unauthorized use may result in civil and criminal liability under copyright law.
This notice is general information, not legal advice.

© {year} BSD CLASS LICENSE · All Rights Reserved
This page was inserted by the watermark generator (not overlaid on content).
"""


def _pick_font(prefer_kr: bool = False) -> fitz.Font:
    paths = []
    if prefer_kr:
        paths += [
            str(Path(__file__).resolve().parent.parent / "ebook-mcpauto/fonts/Pretendard-Regular.ttf"),
            str(Path.home() / "Library/Fonts/Pretendard-Regular.ttf"),
            "/System/Library/Fonts/Supplemental/AppleGothic.ttf",
            "/System/Library/Fonts/AppleSDGothicNeo.ttc",
        ]
    paths += [
        "/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
        "/Library/Fonts/Arial Unicode.ttf",
    ]
    for fp in paths:
        if Path(fp).exists():
            try:
                return fitz.Font(fontfile=fp)
            except Exception:
                continue
    return fitz.Font("helv")


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(
        description="PDF 사선 텍스트 워터마크 + 전용 법률 페이지(2p·마지막) 삽입"
    )
    p.add_argument("input", type=Path, help="원본 PDF 경로")
    p.add_argument("-o", "--output", type=Path, default=None, help="출력 PDF")
    p.add_argument("--text", default=DEFAULT_MARK, help=f'사선 워터마크 문구 (기본: "{DEFAULT_MARK}")')
    p.add_argument("--buyer", default="LICENSED USER", help="구매자/수신자 표기")
    p.add_argument("--order", default="N/A", help="주문·식별번호")
    p.add_argument("--issued", default=None, help="발급일 YYYY-MM-DD")
    p.add_argument("--opacity", type=float, default=0.10, help="사선 워터마크 불투명도 (기본 0.10)")
    p.add_argument("--fontsize", type=float, default=26, help="사선 워터마크 글자 크기 (기본 26, 전체 채움용)")
    p.add_argument("--step", type=float, default=110, help="사선 간격 (기본 110, 작을수록 촘촘·페이지 전체 채움)")
    p.add_argument("--no-legal", action="store_true", help="법률 전용 페이지 생략")
    p.add_argument("--legal-lang", choices=("ko", "en", "both"), default="ko")
    return p.parse_args()


def _make_diagonal_stamp_pdf(
    width: float,
    height: float,
    text: str,
    *,
    fontsize: float,
    opacity: float,
    step: float,
) -> bytes:
    """
    ReportLab으로 **페이지 전체**를 채우는 사선 텍스트 스탬프.
    - 문구끼리 겹치지 않도록 간격 = 문구 폭·줄 간격 기준
    - 모서리까지 커버하도록 페이지 바깥까지 격자를 확장
    좌표계: 좌하단 원점, y↑ / rotate(45) → 화면상 사선
    """
    import math
    from io import BytesIO

    from reportlab.lib.colors import Color
    from reportlab.pdfbase import pdfmetrics
    from reportlab.pdfgen import canvas

    text = (text or DEFAULT_MARK).strip()
    fontsize = max(16.0, min(float(fontsize), 48.0))
    opacity = max(0.05, min(float(opacity), 0.22))
    # 본문 가독성: 밝고 옅게
    gray = min(0.78, max(0.55, 0.50 + opacity * 1.2))

    buf = BytesIO()
    c = canvas.Canvas(buf, pagesize=(width, height))
    c.setFillColor(Color(gray, gray, gray, alpha=opacity))
    c.setFont("Helvetica-Bold", fontsize)

    # 실제 문자열 폭 (겹침 방지 핵심)
    try:
        text_w = float(pdfmetrics.stringWidth(text, "Helvetica-Bold", fontsize))
    except Exception:
        text_w = fontsize * len(text) * 0.55

    # step: UI/CLI 의 "성김" 조절. 작을수록 촘촘.
    # along: 같은 사선 위 다음 문구까지 — 문구 폭보다 약간 크게 (겹침 금지)
    along_gap = max(text_w * 1.18, float(step) * 0.9, text_w + 24)
    # perp: 평행 사선 사이 — 글자 높이보다 충분히 크게
    perp_gap = max(float(step) * 0.55, fontsize * 2.6, 72.0)

    # 45° 단위 벡터 (RL 좌표, rotate(45) 와 동일 방향)
    ang = math.radians(45)
    ux, uy = math.cos(ang), math.sin(ang)  # 사선 진행
    vx, vy = -uy, ux  # 평행선 간격

    # 대각선 길이만큼 격자를 넓게 깔아 모서리까지 채움
    diag = math.hypot(width, height)
    n_perp = int(diag / perp_gap) + 4
    n_along = int(diag / along_gap) + 4

    count = 0
    for i in range(-n_perp, n_perp + 1):
        for j in range(-n_along, n_along + 1):
            cx = width * 0.5 + i * perp_gap * vx + j * along_gap * ux
            cy = height * 0.5 + i * perp_gap * vy + j * along_gap * uy
            # 페이지에서 멀리 떨어진 것은 스킵 (여유는 남김 → 가장자리 채움)
            pad = text_w * 0.6
            if cx < -pad or cx > width + pad or cy < -pad or cy > height + pad:
                continue
            c.saveState()
            c.translate(cx, cy)
            c.rotate(45)
            c.drawCentredString(0, 0, text)
            c.restoreState()
            count += 1

    # 안전장치: 너무 적으면 중앙만이라도
    if count == 0:
        c.saveState()
        c.translate(width * 0.5, height * 0.5)
        c.rotate(45)
        c.drawCentredString(0, 0, text)
        c.restoreState()

    c.save()
    return buf.getvalue()


def add_diagonal_text_watermark(
    page: fitz.Page,
    text: str,
    *,
    opacity: float = 0.10,
    fontsize: float = 28,
    step: float = 120,
    color: tuple[float, float, float] = (0.58, 0.58, 0.58),
) -> None:
    """
    사선 텍스트 워터마크 — 페이지 전체를 균일 타일로 채움 (겹침 없음).
    ReportLab 스탬프 PDF 오버레이 (검색 가능한 텍스트).
    """
    _ = color
    rect = page.rect
    stamp_bytes = _make_diagonal_stamp_pdf(
        rect.width,
        rect.height,
        text,
        fontsize=fontsize,
        opacity=opacity,
        step=step,
    )
    stamp = fitz.open(stream=stamp_bytes, filetype="pdf")
    try:
        page.show_pdf_page(rect, stamp, 0, overlay=True)
    finally:
        stamp.close()

def build_legal_body(mark: str, buyer: str, order: str, issued: str, lang: str) -> str:
    year = issued[:4] if issued and len(issued) >= 4 else str(date.today().year)
    ctx = dict(mark=mark, buyer=buyer, order=order, issued=issued, year=year)
    if lang == "en":
        return LEGAL_EN.format(**ctx)
    if lang == "both":
        return LEGAL_KO.format(**ctx) + "\n\n" + LEGAL_EN.format(**ctx)
    return LEGAL_KO.format(**ctx)


def fill_legal_page(
    page: fitz.Page,
    *,
    mark: str,
    buyer: str,
    order: str,
    issued: str,
    lang: str = "ko",
) -> None:
    """빈 페이지 전체를 법률 전용 레이아웃으로 채움 (본문 위에 덮지 않음)."""
    body = build_legal_body(mark, buyer, order, issued, lang)
    r = page.rect

    # 배경
    sh = page.new_shape()
    sh.draw_rect(r)
    sh.finish(fill=(0.98, 0.97, 0.94), color=None, width=0)
    sh.commit()

    # 상단 바
    header = fitz.Rect(0, 0, r.width, 56)
    sh2 = page.new_shape()
    sh2.draw_rect(header)
    sh2.finish(fill=(0.12, 0.10, 0.08), color=None, width=0)
    sh2.commit()

    page.insert_textbox(
        fitz.Rect(28, 14, r.width - 28, 48),
        f"{mark}  ·  LEGAL NOTICE  ·  BSD",
        fontsize=13,
        fontname="helv",
        color=(0.95, 0.85, 0.45),
        align=fitz.TEXT_ALIGN_LEFT,
    )

    # 본문 영역
    text_box = fitz.Rect(40, 80, r.width - 40, r.height - 48)
    fontfile = None
    for fp in (
        str(Path(__file__).resolve().parent.parent / "ebook-mcpauto/fonts/Pretendard-Regular.ttf"),
        str(Path.home() / "Library/Fonts/Pretendard-Regular.ttf"),
        "/System/Library/Fonts/Supplemental/AppleGothic.ttf",
    ):
        if Path(fp).exists():
            fontfile = fp
            break

    # TextWriter 사용 — CJK 자간/nbsp 이상 삽입 완화
    body = body.replace("\u00a0", " ")
    if fontfile:
        font = fitz.Font(fontfile=fontfile)
        tw = fitz.TextWriter(page.rect)
        tw.fill_textbox(text_box, body, font=font, fontsize=11, align=fitz.TEXT_ALIGN_LEFT)
        tw.write_text(page, color=(0.12, 0.12, 0.12))
    else:
        page.insert_textbox(
            text_box,
            body,
            fontname="helv",
            fontsize=11,
            color=(0.12, 0.12, 0.12),
            align=fitz.TEXT_ALIGN_LEFT,
        )

    # 하단 라인
    page.draw_line(
        fitz.Point(40, r.height - 32),
        fitz.Point(r.width - 40, r.height - 32),
        color=(0.75, 0.65, 0.35),
        width=0.8,
    )


def insert_legal_pages(
    doc: fitz.Document,
    *,
    mark: str,
    buyer: str,
    order: str,
    issued: str,
    lang: str,
) -> list[int]:
    """
    새 페이지를 삽입한다 (오버레이 아님).
    - 1페이지 뒤(인덱스 1) → 문서상 2페이지
    - 맨 끝 → 마지막 페이지
    반환: 1-based 페이지 번호 목록
    """
    # 기준 용지 크기: 첫 페이지
    w, h = doc[0].rect.width, doc[0].rect.height

    # 1) 표지 다음에 법률 페이지
    doc.new_page(pno=1, width=w, height=h)
    fill_legal_page(doc[1], mark=mark, buyer=buyer, order=order, issued=issued, lang=lang)

    # 2) 맨 끝 법률 페이지
    doc.new_page(width=w, height=h)  # append
    last = doc.page_count - 1
    fill_legal_page(doc[last], mark=mark, buyer=buyer, order=order, issued=issued, lang=lang)

    # 법률 전용 페이지에도 동일 전체 채움 사선 (본문보다 약간 옅게)
    add_diagonal_text_watermark(doc[1], mark, opacity=0.07, fontsize=24, step=110)
    add_diagonal_text_watermark(doc[last], mark, opacity=0.07, fontsize=24, step=110)

    return [2, doc.page_count]  # 1-based: page 2 and last


def process_pdf(
    src: Path,
    dst: Path,
    *,
    text: str,
    buyer: str,
    order: str,
    issued: str,
    opacity: float,
    fontsize: float,
    step: float,
    add_legal: bool,
    legal_lang: str,
) -> dict:
    doc = fitz.open(src)
    if doc.page_count < 1:
        doc.close()
        raise SystemExit("PDF에 페이지가 없습니다.")

    content_pages = doc.page_count

    # 1) 기존 본문 페이지에만 사선 워터마크
    for i in range(content_pages):
        add_diagonal_text_watermark(
            doc[i],
            text,
            opacity=opacity,
            fontsize=fontsize,
            step=step,
        )

    legal_pages_1based: list[int] = []
    if add_legal:
        legal_pages_1based = insert_legal_pages(
            doc,
            mark=text,
            buyer=buyer,
            order=order,
            issued=issued,
            lang=legal_lang,
        )

    dst.parent.mkdir(parents=True, exist_ok=True)
    doc.save(dst, garbage=4, deflate=True, clean=True)
    total = doc.page_count
    doc.close()
    return {
        "pages": total,
        "content_pages": content_pages,
        "legal_pages_1based": legal_pages_1based,
        "output": str(dst),
    }


def main() -> None:
    args = parse_args()
    src = args.input.expanduser().resolve()
    if not src.exists():
        print(f"입력 파일 없음: {src}", file=sys.stderr)
        sys.exit(1)
    if src.suffix.lower() != ".pdf":
        print("PDF 파일만 지원합니다.", file=sys.stderr)
        sys.exit(1)

    out = args.output
    if out is None:
        out = src.with_name(src.stem + "_watermarked.pdf")
    else:
        out = out.expanduser().resolve()

    issued = args.issued or date.today().isoformat()

    info = process_pdf(
        src,
        out,
        text=args.text.strip() or DEFAULT_MARK,
        buyer=args.buyer,
        order=args.order,
        issued=issued,
        opacity=args.opacity,
        fontsize=args.fontsize,
        step=args.step,
        add_legal=not args.no_legal,
        legal_lang=args.legal_lang,
    )
    print("OK")
    print(f"  pages:         {info['pages']} (content {info['content_pages']} + legal pages)")
    print(f"  watermark:     {args.text} (diagonal text, no image, opacity={args.opacity})")
    print(f"  legal pages:   {info['legal_pages_1based'] or '(none)'}  ← NEW pages, not overlay")
    print(f"  output:        {info['output']}")


if __name__ == "__main__":
    main()
