#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Pandoc専用：MarkdownのWord/PowerPoint/HTML変換とテンプレート生成。
- AI処理は一切ありません（tkai_lib不要）
- 例:
    # テンプレート生成
    python pandoc_convert.py --make_template docx --template template_textbook.docx
    python pandoc_convert.py --make_template pptx --template template_slide.pptx

    # 変換
    python pandoc_convert.py --convert docx --infile lecture_textbook.md --template template_textbook.docx
    python pandoc_convert.py --convert pptx --infile lecture_slide.md --template template_slide.pptx
    python pandoc_convert.py --convert html --infile lecture_textbook.md --mathml
    python pandoc_convert.py --convert html --infile lecture_textbook.md --css custom_style.css
"""

import os
import sys
import argparse
from pathlib import Path
import re
import yaml
import shutil
import platform
import subprocess
from pathlib import Path
import chardet

pause = 0

def terminate():
    if pause:
        input("\nPress ENTER to terminate\n")
    exit()

def find_pandoc() -> str | None:
    """
    pandoc 実行ファイルのパスを探す。
    優先順位:
    1. 環境変数 pandoc_path
    2. 環境変数 tkProg_path に基づく既定配置
       - Windows: {tkProg_path}/tkApp_Win/pandoc/pandoc.exe
       - Linux:   {tkProg_path}/tkApp_Linux/pandoc/pandoc
    3. PATH 環境変数から探す
    見つからなければ None を返す。
    """

    # 1. 環境変数 pandoc_path
    env_pandoc = os.environ.get("pandoc_path")
    if env_pandoc and os.path.isfile(env_pandoc):
        return env_pandoc

    # 2. tkProg_path 内
    tkprog = os.environ.get("tkProg_path")
    if tkprog is None:
        script_dir = Path(sys.argv[0]).resolve().parent
        tkprog = (script_dir / "../../").resolve()
    if tkprog:
        if platform.system() == "Windows":
            candidate = os.path.join(tkprog, "tkApp_Win", "pandoc", "pandoc.exe")
        else:
            candidate = os.path.join(tkprog, "tkApp_Linux", "pandoc", "pandoc")
        if os.path.isfile(candidate):
            return candidate

    # 3. PATH から探す
    exe_name = "pandoc.exe" if platform.system() == "Windows" else "pandoc"
    path = shutil.which(exe_name)
    if path:
        return path

    return "pandoc"

def find_template(template_path: str) -> str | None:
    """
    template_path で与えられたファイル名を以下の順に探す:
    1. 実行ディレクトリ (カレントディレクトリ)
    2. スクリプトのあるディレクトリ (sys.argv[0] 基準)

    見つかればフルパスを返し、見つからなければ None を返す。
    """
    if not template_path:
        return None

    # 1. 実行ディレクトリ（カレント）
    candidate1 = Path.cwd() / template_path
    if candidate1.is_file():
        return str(candidate1.resolve())

    # 2. スクリプトのあるディレクトリ
    script_dir = Path(sys.argv[0]).resolve().parent
    candidate2 = script_dir / template_path
    if candidate2.is_file():
        return str(candidate2.resolve())

    # 見つからなければ None
    return template_path


def parse_args():
    pandoc_path = find_pandoc()
    print(f"✅ pandoc found: {pandoc_path}")

    p = argparse.ArgumentParser(
        description="Pandocユーティリティ（テンプレート生成 & 変換専用）",
        formatter_class=argparse.RawTextHelpFormatter
    )
    p.add_argument('--pandoc_path', "-p", default=pandoc_path, help=f'pandoc 実行ファイルのパス (デフォルト {pandoc_path})')
    p.add_argument('--infile',      "-i", help='変換元Markdown（--convert使用時に指定）')
    p.add_argument('--outfile',     "-o", type=str, default = None, help='出力ファイル名n（デフォルト --convertから拡張子を設定）')
    p.add_argument('--template',    "-t", default = None, help='変換に使用、または作成するテンプレートファイルのパス')
    p.add_argument('--convert',     "-c", choices=['docx', 'pptx', 'html', ''], help='Pandoc変換を実行')
    p.add_argument('--toc',    type=int, default=0, help='入力を markdown-yaml_metadata_block として解釈する')
    p.add_argument('--mathml', action='store_true', help='HTML変換時にMathMLを使って数式を表示')
    p.add_argument('--css',    help='HTML変換時のカスタムCSS')
    p.add_argument('--make_template', choices=['docx', 'pptx'], help='デフォルトテンプレートを出力して終了')
    p.add_argument('--smart_conversion', type=int, default=0, help='自動変換 (--を-に変換するなど) する')
    p.add_argument('--no-yaml', action='store_true', help='入力を markdown-yaml_metadata_block として解釈する')
    p.add_argument('--verbose', action='store_true', help='詳細出力')
    p.add_argument("--pause",   type=int, default=0, help="終了時にENTERキー入力を要求するか (デフォルト: 0)")
    """
    p.add_argument(
        '--pandoc_path',
        default=None,
        help='pandoc 実行ファイルのパス'
    )
    p.add_argument(
        '--template',
        default=None,
        help='pandoc 用テンプレート (docx/pptx)'
    )
    p.add_argument(
        '--toc',
        action='store_true',
        help='pandoc: --toc'
    )
    p.add_argument(
        '--css',
        default=None,
        help='pandoc: HTML 用 CSS'
    )
    """

    args = p.parse_args()

    if args.template:
        args.template = find_template(args.template)
        print(f"✅ template found: {args.template}")

    return p, args

def run_command(cmd: list) -> bool:
    print(f"⚡️ 実行中: {' '.join(cmd)}")
    try:
        _ = subprocess.run(cmd, check=True, capture_output=True, text=True, encoding='utf-8')
        print("✅ コマンドは正常に完了しました。")
        return True
    except FileNotFoundError:
        print(f"❌ '{cmd[0]}' が見つかりません。Pandocはインストール済みでPATHが通っていますか？", file=sys.stderr)
    except subprocess.CalledProcessError as e:
        print(f"❌ Pandoc実行エラー (終了コード: {e.returncode})", file=sys.stderr)
        print(f"--- stderr ---\n{e.stderr}\n--------------", file=sys.stderr)
    return False

def extract_yaml_front_matter(md_text: str):
    """
    Markdown の先頭に YAML フロントマターがあれば抽出する。
    """
    pattern = r'^---\s*\n(.*?)\n---\s*\n'
    match = re.match(pattern, md_text, re.DOTALL)
    if not match:
        return None
    return match.group(1)

def is_valid_yaml(yaml_text: str) -> bool:
    """
    YAML として正しいかどうかを判定する。
    """
    try:
        yaml.safe_load(yaml_text)
        return True
    except Exception:
        return False

def read_text_with_chardet(path):
    # まずバイナリで読み込んで文字コードを推定
    if not os.path.isfile(path): return None
    
    with open(path, "rb") as fb:
        raw = fb.read()

    result = chardet.detect(raw)
    encoding = result["encoding"]
    confidence = result["confidence"]

    if encoding is None:
        print(f"Failed to detect encoding: {encoding}")
        print(f"  Try utf-8")
        encoding = 'utf-8'
    else:
        print(f"Detected encoding: {encoding} (confidence={confidence}) for {path}")

    # 推定した文字コードでデコード（失敗しにくいように errors='replace' も可）
    try:
        text = raw.decode(encoding, errors="replace")
    except:
        print(f"Error in read_text_with_chardet(): Failed to encode with {encoding}")
        return None
    return text


def correct_markdown_pandoc_error(md_text: str, base_dir: Path = None) -> str:
    """
    pandoc がエラーを出す典型的な Markdown の問題を自動修正する。
    - YAML と誤判定される `---` の前後に空行を追加（ただし本物の YAML は保持）
    - 存在しない画像・リンクを無害化（コード化）
    - $$ inline $$ のような1行ブロック数式を、pandoc が確実に読めるブロック形式に正規化
    - 生成AIがよく出す $[ ... ]$ を $$ ... $$ に変換
    """

    # ============================================================
    # 1. YAML フロントマターの判定
    # ============================================================
    yaml_block = extract_yaml_front_matter(md_text)

    if yaml_block is not None:
        if is_valid_yaml(yaml_block):
            # 本物の YAML → 何もしない
            pass
        else:
            # YAML として不正 → pandoc が誤判定するので空行を追加して無効化
            md_text = md_text.replace("---", "\n---\n", 1)

    # ============================================================
    # 2. 単独の --- の前後に空行を追加（YAML 以外）
    # ============================================================
    lines = md_text.splitlines()
    fixed_lines = []
    for i, line in enumerate(lines):
        if line.strip() == "---":
            # YAML フロントマター以外の --- を安全化
            if i > 0 and lines[i-1].strip() != "":
                fixed_lines.append("")
            fixed_lines.append(line)
            if i < len(lines)-1 and lines[i+1].strip() != "":
                fixed_lines.append("")
        else:
            fixed_lines.append(line)

    md_text = "\n".join(fixed_lines)

    # ============================================================
    # 3. 存在しない画像・リンクを無害化
    # ============================================================
    def replace_if_missing(match):
        full = match.group(1)
        path = match.group(2)

        if base_dir is None:
            return full

        file_path = (base_dir / path).resolve()
        if not file_path.exists():
            # pandoc が落ちるのでコード化
            return f"`{full}`"
        return full

    pattern = r'(!?\[.*?\]\((.*?)\))'
    md_text = re.sub(pattern, replace_if_missing, md_text)

    # ============================================================
    # 4. $$ inline $$ → ブロック数式に正規化
    # ============================================================
    def normalize_inline_math(match):
        content = match.group(1).strip()
        # すでに複数行ならそのままブロック化
        return f"$$\n{content}\n$$"

    math_pattern = r'\$\$(.+?)\$\$'
    md_text = re.sub(math_pattern, normalize_inline_math, md_text, flags=re.DOTALL)

    # ============================================================
    # 5. $[ ... ]$ → $$ ... $$ に変換（生成AI対策）
    # ============================================================
    def convert_ai_math(match):
        content = match.group(1).strip()
        return f"$$\n{content}\n$$"

#    ai_math_pattern = r'\$\[\s*(.+?)\s*\]\$'      # $[ ... ]$
#    md_text = re.sub(ai_math_pattern, convert_ai_math, md_text, flags=re.DOTALL)
#    ai_math_pattern = r'\$\(\s*(.+?)\s*\)\$'      # $( ... )$
#    md_text = re.sub(ai_math_pattern, convert_ai_math, md_text, flags=re.DOTALL)
    ai_math_pattern = r'\\\[\s*(.+?)\s*\\\]'      # \[ ... \]
    md_text = re.sub(ai_math_pattern, convert_ai_math, md_text, flags=re.DOTALL)
    ai_math_pattern = r'\\\(\s*(.+?)\s*\\\)'      # \( ... \)
    md_text = re.sub(ai_math_pattern, convert_ai_math, md_text, flags=re.DOTALL)

    return md_text

def make_pandoc_template(pandoc_path: str, fmt: str, template_file: str):
    print(f"🎨 {fmt.upper()} 用のPandocテンプレートを作成します...")
    data_file = 'reference.docx' if fmt == 'docx' else 'reference.pptx'
    cmd = [pandoc_path, '-o', template_file, f'--print-default-data-file={data_file}']
    
    if run_command(cmd):
        print(f"📄 テンプレート '{template_file}' を作成しました。編集してスタイルを調整してください。")

def correct_html(outfile):
    with open(outfile, 'r', encoding='utf-8') as f:
        body_content = f.read()

# MathJax対応のHTMLとしてラップ
    html_template = f"""<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
  <script id="MathJax-script" async
    src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/mml-chtml.js"></script>
</head>

<body>
{body_content}
</body>
</html>
"""

    with open(outfile, 'w', encoding='utf-8') as f:
        f.write(html_template)

def convert_with_pandoc(pandoc_path: str, target: str, infile_md: str, outfile: str, 
                template: str = None, css: str = None, no_yaml: bool = False, args = None):
    print()
    print(f"🔄 '{infile_md}' → {target.upper()} 変換を開始します...")
    if not Path(infile_md).exists():
        print(f"❌ 入力Markdown '{infile_md}' が存在しません。", file=sys.stderr)
        terminate()

    print(f"target  : {target}")
    print(f"template: {template}")

    if outfile is None or outfile == '':
        outfile = str(Path(infile_md).with_suffix(f'.{target}'))
    # ベース引数
    if no_yaml:
        cmd = [pandoc_path, infile_md, '-o', outfile, '-f', 'markdown-yaml_metadata_block']
    else:
        cmd = [pandoc_path, infile_md, '-o', outfile]

    if args.toc and outfile.endswith(".docx"):
        cmd.append('--toc')
    if args.mathml:
        cmd.append('--mathml')
    if args.verbose:
        cmd.append('--verbose')
    if not args.smart_conversion:
        cmd.extend(['-f', 'markdown-smart'])

    # 出力形式ごとの追加
    if target == 'html':
        if css:
            cmd.extend(['--css', css])
    elif target in ('docx', 'pptx'):
        if not template:
            pass
#            print("❌ --convert docx/pptx では --template が必須です。", file=sys.stderr)
#            sys.exit(1)
        elif f".{target}" in template:
            cmd.extend(['--reference-doc', template])

#        if target == 'pptx':
#            cmd.extend(['-t', 'pptx'])
        cmd.extend(['-t', target])

    print("cmd:", cmd)
    ret = run_command(cmd)
    if ret:
        if args.convert == 'html':
            correct_html(outfile)
        
        print(f"📑 変換後のファイル '{outfile}' を作成しました。")

def convert_md(
    infile_md,
    target,                 # 'docx' | 'pptx' | 'html'
    pandoc_path,            # ← 明示的に渡す
    outfile=None,
    template=None,
    toc=False,
    css=None,
    mathml=False,
    no_yaml=False,
    verbose=False,
):
    """
    Library API for md -> docx/pptx/html
    """

    class DummyArgs:
        pass

    args = DummyArgs()
    args.toc = toc
    args.mathml = mathml
    args.verbose = verbose
    args.convert = target

    # read & normalize markdown
    md_text = read_text_with_chardet(infile_md)
    if md_text is None:
        raise RuntimeError(f"Failed to read {infile_md}")

    corrected = correct_markdown_pandoc_error(
        md_text,
        base_dir=Path(infile_md).parent
    )

    corrected_path = Path(infile_md).with_suffix(".corrected.md")
    corrected_path.write_text(corrected, encoding="utf-8")

    convert_with_pandoc(
        pandoc_path=pandoc_path,
        target=target,
        infile_md=str(corrected_path),
        outfile=outfile,
        template=template,
        css=css,
        no_yaml=no_yaml,
        args=args,
    )

    return True

def main():
    global pause
    
    parser, args = parse_args()
    pause = args.pause
    print()
    print(f"{args.pandoc_path=}")
    if args.pandoc_path == "":
        print(f"Error: pandoc実行ファイルを引数 pandoc_path で設定してください")
        terminate()
    if not os.path.exists(args.pandoc_path):
        print(f"Error: pandoc実行ファイル [{args.pandoc_path}] が見つかりません")
        terminate()

    if args.convert == '':
        target = os.path.splitext(args.outfile)[1].lstrip('.')
    else:
        target = args.convert

    print(f"{args.infile=}")
    print(f"{args.outfile=}")
    print(f"{args.template=}")
    print(f"{target=}")
    print(f"{args.toc=}")
    print(f"{args.pause=}")

    # 1) テンプレート生成（最優先）
    if args.make_template:
        if not args.template:
            parser.error("--make_template には --template で出力先ファイル名が必要です。")
        make_pandoc_template(args.pandoc_path, args.make_template, args.template)
        terminate()

    # 2) 変換
    if args.convert or args.outfile:
        if not args.infile:
            parser.error("--convert を使うときは --infile（Markdown）を指定してください。")

        md_text = read_text_with_chardet(args.infile)

        if md_text:
            text_corrected = correct_markdown_pandoc_error(md_text, base_dir = None)
            corrected_path = Path(args.infile).with_suffix(".corrected.md")
            with open(corrected_path, "w", encoding="utf-8") as fp:
                fp.write(text_corrected)
        else:
            corrected_path = args.infile

        convert_with_pandoc(
            args.pandoc_path,
            target,
            str(corrected_path),
            args.outfile,
            template=args.template,
            css=args.css,
            no_yaml=args.no_yaml,
            args = args,
        )
        terminate()

    # 3) どちらも指定がない場合
    print("\nError: --convertか--outfileを指定してください")
    parser.print_help()

if __name__ == "__main__":
    main()
    terminate()
