#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import base64
import os
import re
import sys
import logging
from pathlib import Path
from typing import Optional

# 패키지 루트를 sys.path에 추가하여 절대 임포트 통일
_project_root = Path(__file__).resolve().parents[1]
if str(_project_root) not in sys.path:
    sys.path.insert(0, str(_project_root))

# 의존성 확인 및 설치
import importlib.util

spec = importlib.util.spec_from_file_location(
    "requirements_rnac", os.path.join(os.path.dirname(__file__), "requirements_rnac.py")
)
requirements_rnac = importlib.util.module_from_spec(spec)
spec.loader.exec_module(requirements_rnac)
requirements_rnac.check_and_install_dependencies()

import pypandoc

logging.basicConfig(level=logging.INFO, format="%(message)s")


def embed_images_as_base64(html_text: str, base_dir: str) -> str:
    """
    HTML의 로컬 이미지 경로를 base64 인코딩하여 임베딩.
    Base64로 이미 인코딩된 이미지(data:image/...)는 건드리지 않음.

    Args:
        html_text: 원본 HTML 텍스트
        base_dir: 이미지 파일 기준 디렉토리

    Returns:
        이미지가 base64로 임베딩된 HTML 텍스트
    """

    def replace_img(match):
        img_path = match.group(1)

        # 이미 Base64로 인코딩된 이미지는 건드리지 않음
        if img_path.startswith("data:"):
            return match.group(0)

        img_path = img_path.replace("/", os.sep).replace("\\", os.sep)

        if os.path.isabs(img_path):
            full_path = img_path
        else:
            full_path = os.path.join(base_dir, img_path)

        full_path = os.path.normpath(full_path)

        if not os.path.isfile(full_path):
            logging.warning(f"이미지 파일 없음: {full_path}")
            return match.group(0)

        ext = os.path.splitext(full_path)[1].lower()
        mime_map = {
            ".png": "image/png",
            ".jpg": "image/jpeg",
            ".jpeg": "image/jpeg",
            ".gif": "image/gif",
            ".svg": "image/svg+xml",
            ".bmp": "image/bmp",
        }
        mime_type = mime_map.get(ext, "image/png")

        with open(full_path, "rb") as f:
            img_data = base64.b64encode(f.read()).decode("utf-8")

        logging.info(f"이미지 임베딩: {os.path.basename(full_path)}")
        return f'<img src="data:{mime_type};base64,{img_data}"'

    pattern = r'<img src="([^"]+)"'
    return re.sub(pattern, replace_img, html_text)


def clean_html_for_pandoc(html_text: str) -> str:
    """
    Pandoc 변환을 위해 HTML 정리: KaTeX 스크립트/링크 제거 및 코드블록 래핑.

    <pre><code> 블록을 custom-style="Code Fence" Div로 래핑하여
    DOCX 변환 시 Code Fence 스타일(테두리 박스)이 적용되도록 합니다.

    Args:
        html_text: 원본 HTML 텍스트

    Returns:
        정리된 HTML 텍스트
    """
    html_text = re.sub(r"<link[^>]*katex[^>]*>", "", html_text, flags=re.IGNORECASE)
    html_text = re.sub(
        r"<script[^>]*katex[^>]*>.*?</script>", "", html_text, flags=re.IGNORECASE | re.DOTALL
    )
    html_text = re.sub(
        r"<script[^>]*mermaid[^>]*>.*?</script>", "", html_text, flags=re.IGNORECASE | re.DOTALL
    )

    # <pre><code ...>...</code></pre> 블록을 custom-style="Code Fence" Div로 래핑
    # pandoc은 <pre> 블록을 SourceCode로 처리하므로, 줄별 <p>로 분해하여 래핑해야
    # custom-style 속성이 정상 적용됨
    def wrap_pre_block(match: re.Match) -> str:
        inner = match.group(1)
        # HTML 엔티티 복원 (pandoc이 plain text로 전달받도록)
        inner = inner.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", '"')
        # 줄별로 <p> 태그로 감싸기 (빈 줄은 공백 유지)
        lines = inner.split("\n")
        # 마지막 빈 줄 제거
        while lines and not lines[-1].strip():
            lines.pop()

        def preserve_indent(line: str) -> str:
            """선행 공백/탭을 &#160;(NBSP)로 치환하여 DOCX에서 들여쓰기 보존."""
            if not line:
                return "&#160;"
            # 선행 공백 문자(스페이스·탭) 개수 계산 후 NBSP로 치환
            stripped = line.lstrip()
            indent = line[: len(line) - len(stripped)]
            # 탭은 4 NBSP, 스페이스는 1 NBSP로 변환
            nbsp_indent = indent.replace("\t", "&#160;&#160;&#160;&#160;").replace(" ", "&#160;")
            return nbsp_indent + stripped

        p_lines = "".join(f"<p>{preserve_indent(line)}</p>" for line in lines)
        return f'<div custom-style="Code Fence">{p_lines}</div>'

    html_text = re.sub(
        r"<pre><code[^>]*>(.*?)</code></pre>",
        wrap_pre_block,
        html_text,
        flags=re.DOTALL,
    )

    # 블록 수식 div (text-align:center) → custom-style="Image Center"
    # 패턴: <div style="text-align: center; ..."><img ... /></div>
    html_text = re.sub(
        r'<div style="text-align: center;[^"]*">(<img [^>]+/>)</div>',
        r'<div custom-style="Image Center">\1</div>',
        html_text,
    )

    return html_text


# 패키지 내장 reference.docx 경로 (Code Fence 스타일 정의 포함)
_REFERENCE_DOCX = os.path.join(os.path.dirname(__file__), "reference.docx")


def html_to_doc(html_path: str, output_path: str) -> None:
    """
    HTML 파일을 DOCX로 변환 (이미지/수식 임베딩).

    Args:
        html_path: 입력 HTML 파일 경로
        output_path: 출력 DOCX 파일 경로
    """
    logging.info(f"HTML 읽기: {html_path}")
    with open(html_path, "r", encoding="utf-8") as f:
        html_text = f.read()

    base_dir = os.path.dirname(os.path.abspath(html_path))

    logging.debug("이미지 임베딩 중...")
    html_text = embed_images_as_base64(html_text, base_dir)

    logging.debug("HTML 정리 중...")
    html_text = clean_html_for_pandoc(html_text)

    logging.debug("DOCX 변환 중...")
    extra_args = ["--standalone"]
    if os.path.isfile(_REFERENCE_DOCX):
        extra_args.append(f"--reference-doc={_REFERENCE_DOCX}")
    pypandoc.convert_text(
        html_text, "docx", format="html", outputfile=output_path, extra_args=extra_args
    )

    logging.info(f"변환 완료: {output_path}")


def main():
    parser = argparse.ArgumentParser(
        description="HTML(.html)을 DOCX로 변환합니다 (이미지/수식 임베딩)."
    )
    parser.add_argument("input", help="입력 HTML 파일 경로 (.html)")
    parser.add_argument("-o", "--output", help="출력 DOCX 파일 경로 (.docx)")
    args = parser.parse_args()

    in_path = args.input
    if not os.path.isfile(in_path):
        print(f"파일을 찾을 수 없습니다: {in_path}", file=sys.stderr)
        sys.exit(1)

    out_path = args.output or os.path.splitext(in_path)[0] + ".docx"
    html_to_doc(in_path, out_path)


if __name__ == "__main__":
    main()
