#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import argparse
import shutil
import subprocess
import traceback
from pathlib import Path
from datetime import datetime


SCRIPT_FULLPATH = os.path.abspath(sys.argv[0])
SCRIPT_DIR = os.path.dirname(SCRIPT_FULLPATH)
SCRIPT_BASENAME = os.path.splitext(os.path.basename(SCRIPT_FULLPATH))[0]

ADD_DOCSTRING_PATH = os.path.join(SCRIPT_DIR, "add_docstring.py")
EXPLAIN_PROGRAM_PATH = os.path.join(SCRIPT_DIR, "explain_program5.py")


def relative_from_source(argv0: str) -> str:
    p = Path(argv0).resolve()
    parts = p.parts

    try:
        idx = parts.index("source")
    except ValueError:
        return ""

    rel_path = Path(*parts[idx + 1:])
    return str(rel_path.parent)


def run_step(message, cmd_list):
    print(f"\n>>> {message}")
    print(f"    コマンド: {' '.join(cmd_list)}")
    try:
        result = subprocess.run(cmd_list, text=True, errors="ignore")
        if result.returncode != 0:
            print("!!! エラーが発生しました")
            return False
        return True
    except Exception as e:
        print(f"!!! 実行エラー: {e}")
        return False


def make_init_py(path):
    if os.path.exists(path):
        print(f">>> Step: {path} が見つかりました。")
    else:
        print(f">>> Step: {path} を作成中...")
        with open(path, "w", encoding="utf-8") as f:
            f.write("")
        print(f"    Done: {path}")


def make_index_template(path, module_path):
    if os.path.exists(path):
        print(f">>> Step: {path} に追加中...")
        with open(path, "a", encoding="utf-8") as f:
            f.write(f"   {module_path}_index\n")
        print(f"    Done: {path}")
    else:
        print(f">>> Step: {path} を作成中...")
        content = f"""\
プロジェクト全体ドキュメント
============================

.. toctree::
   :maxdepth: 2
   :caption: メインメニュー:
   :hidden:
   :glob:

   {module_path}_index
"""
        with open(path, "w", encoding="utf-8") as f:
            f.write(content)
        print(f"    Done: {path}")


def make_index_rst(index_rst, base_name, package_path):
    print(f">>> Step: {index_rst} を作成中...")
    index_content = f"""{base_name} ドキュメント
============================================================================

.. toctree::
   :maxdepth: 1

   {base_name}_download
   {base_name}_usage
   {base_name}_quality
   {base_name}_examples
   {base_name}_api
"""
    with open(index_rst, "w", encoding="utf-8") as f:
        f.write(index_content)
    print(f"    Done: {index_rst}")


def make_download_rst(download_rst, base_name, script_rel_path):
    print(f">>> Step: {download_rst} を作成中...")

    script_name = os.path.basename(script_rel_path)

    download_content = f"""{script_name} ダウンロード/コピー
========================================================

:download:`{script_name} をダウンロード <{script_rel_path}>`

.. dropdown:: {script_name}
   :color: primary
   :icon: file-code

   .. literalinclude:: {script_rel_path}
      :language: python
      :linenos:
      :caption: {script_name}
"""
    with open(download_rst, "w", encoding="utf-8") as f:
        f.write(download_content)

    print(f"    Done: {download_rst}")


def make_api_rst(api_rst, base_name, package_path):
    print(f">>> Step: {api_rst} を作成中...")
    api_content = f"""{base_name} プログラム仕様
============================================================================

.. currentmodule:: {package_path}

.. automodule:: {package_path}
   :members:
   :undoc-members:
   :show-inheritance:
"""
    with open(api_rst, "w", encoding="utf-8") as f:
        f.write(api_content)
    print(f"    Done: {api_rst}")


def make_examples_md(examples_md, infile, base_name):
    print(f">>> Step: {examples_md} テンプレートを作成中...")

    image_files = sorted(
        f for f in os.listdir(".")
        if f.startswith(base_name) and f.lower().endswith((".png", ".jpg", ".jpeg"))
    )

    data_files = sorted(
        f for f in os.listdir(".")
        if f.startswith(base_name) and f.lower().endswith((".csv", ".xlsx", ".xls", ".txt"))
    )

    print("Image files:", image_files)
    print("Data files:", data_files)

    print("  help logを取得します")
    result = subprocess.run(
        [sys.executable, infile, "--help"],
        text=True,
        capture_output=True,
        errors="ignore",
    )

    print("    return code:", result.returncode)

    if result.returncode == 0:
        help_log = result.stdout + "\n" + result.stderr
    else:
        help_log = "(ヘルプの自動取得に失敗しました。ここに実行ログを貼り付けてください)"

    if data_files:
        data_section = "## データファイル\n"
        for df in data_files:
            data_section += f"- [{df}](./{df})\n"
        data_section += "\n"
    else:
        data_section = "## 生成されたデータファイル\n（データファイルが見つかりませんでした）\n\n"

    if image_files:
        image_section = "## 画像ファイル\n\n"
        for img in image_files:
            image_section += f"- [{img}](./{img})\n"
            image_section += f"![{img}](./{img})\n\n"
    else:
        image_section = "## 生成された画像一覧\n（画像ファイルが見つかりませんでした）\n\n"

    examples_content = f"""# {base_name} 実行例

## help出力 `{base_name}.py --help`

<pre style="background-color: #f4f4f4; border: 1px solid #ccc; padding: 10px; border-radius: 5px; font-family: 'Courier New', Courier, monospace; overflow-x: auto;">
{help_log}
</pre>

{data_section}

{image_section}
"""

    with open(examples_md, "w", encoding="utf-8") as f:
        f.write(examples_content)

    print(f"    Done: {examples_md}")


def check_updated(file1, file2):
    if not os.path.exists(file1):
        return False
    if not os.path.exists(file2):
        return True

    t1 = os.path.getmtime(file1)
    dt1 = datetime.fromtimestamp(t1)
    t2 = os.path.getmtime(file2)
    dt2 = datetime.fromtimestamp(t2)

    print(f"File stamps: {file1} : {dt1}")
    print(f"             {file2} : {dt2}")

    if t1 >= t2:
        return True
    return False


def main(args):
    infile = args.infile

    if not os.path.exists(infile):
        print(f"エラー: 入力ファイル '{infile}' が見つかりません。")
        return

    base_name = os.path.splitext(os.path.basename(infile))[0]
    date_str = datetime.now().strftime("%Y%m%d")

    init_py = "__init__.py"
    index_template = "index.template"
    docstring_out = f"{base_name}_docstring.py"
    backup_file = f"{base_name}_{date_str}.py"

    usage_md = f"{base_name}_usage.md"
    quality_md = f"{base_name}_quality.md"
    examples_md = f"{base_name}_examples.md"
    index_rst = f"{base_name}_index.rst"
    download_rst = f"{base_name}_download.rst"
    api_rst = f"{base_name}_api.rst"

    if args.subdir is not None and args.subdir != "":
        module_path = os.path.join(args.subdir, base_name)
        package_path = f"{args.subdir}.{base_name}"
    else:
        module_path = base_name
        package_path = module_path

    print("=" * 60)
    print(f" プロジェクト: {base_name} のSphinxファイル自動生成を開始します")
    print(f"    モジュールパス: {module_path}")
    print(f"    パッケージパス: {package_path}")
    print("=" * 60)

    make_init_py(init_py)

    if os.path.exists(index_template):
        print(f"** Warning: [{index_template}] exists. Skip to create.")
    else:
        make_index_template(index_template, module_path)

    if args.update_index:
        print(f"** Message: [Force update {index_rst} due to {args.update_index=}].")
        make_index_rst(index_rst, base_name, package_path)
    elif os.path.exists(index_rst):
        print(f"** Warning: [{index_rst}] exists. Skip to create.")
    else:
        make_index_rst(index_rst, base_name, package_path)

    if os.path.exists(download_rst):
        print(f"** Warning: [{download_rst}] exists. Skip to create.")
    else:
        make_download_rst(download_rst, base_name, infile)

    if os.path.exists(api_rst):
        print(f"** Warning: [{api_rst}] exists. Skip to create.")
    else:
        make_api_rst(api_rst, base_name, package_path)

    if os.path.exists(examples_md):
        print(f"** Warning: [{examples_md}] exists. Skip to create.")
    else:
        make_examples_md(examples_md, infile, base_name)

    args_list = [
        "--api", args.api,
        "--update", str(args.update),
        "--overwrite", str(args.overwrite),
        "--pause", str(args.pause),
    ]

    if not check_updated(infile, usage_md):
        print(f"Step EXPLAIN_PROGRAM_PATH: {infile}が{usage_md}より古いのでスキップします")
    else:
        if not run_step(
            "Step: EXPLAIN_PROGRAM_PATH を実行してusageを生成中...",
            [
                sys.executable,
                EXPLAIN_PROGRAM_PATH,
                infile,
                usage_md,
                "--inifile",
                args.prompt_explain,
                *args_list,
            ],
        ):
            return

    if not check_updated(infile, docstring_out):
        print(f"Step ADD_DOCSTRING_PATH: {infile}が{docstring_out}より古いのでスキップします")
    else:
        if not run_step(
            "Step: ADD_DOCSTRING_PATH を実行してDocstringを追加中...",
            [
                sys.executable,
                ADD_DOCSTRING_PATH,
                infile,
                "--inifile",
                args.prompt_add_docstring,
                *args_list,
            ],
        ):
            return

        print(">>> Step: オリジナルファイルのバックアップを作成中...")
        if os.path.exists(infile):
            shutil.copy2(infile, backup_file)
            print(f"    Done: {infile} -> {backup_file}")
        else:
            print("!!! バックアップ対象のファイルが見つかりません。")
            return

        if os.path.exists(docstring_out) and os.path.exists(infile):
            t_doc = os.path.getmtime(docstring_out)
            size_byte = os.path.getsize(docstring_out)
            t_in = os.path.getmtime(infile)

            dt_doc = datetime.fromtimestamp(t_doc)
            dt_in = datetime.fromtimestamp(t_in)

            print(f"File stamps: {docstring_out} : {dt_doc} ({size_byte} bytes)")
            print(f"             {infile} : {dt_in}")

            if size_byte == 0:
                print(f"  {docstring_out} はダミーファイルのため、ファイルコピーは行いません")
            elif t_doc <= t_in:
                print("  infile の方が新しいのでファイルコピーは行いません")
            else:
                print(f">>> Step: 生成されたDocstring版ファイルを {infile} にリネーム中...")
                if os.path.exists(docstring_out):
                    os.replace(docstring_out, infile)
                    print(f"    Done: {docstring_out} -> {infile}")

                    with open(docstring_out, "w", encoding="utf-8") as fp:
                        fp.write("")

                    print(f"    ダミーの空ファイル {docstring_out} を作りました")
                else:
                    print("!!! Docstring版ファイルが生成されていなかったため、リネームをスキップします。")
                    return

    if args.prompt_quality:
        if not run_step(
            "Step: EXPLAIN_PROGRAM_PATH を実行してコード品質評価を生成中...",
            [
                sys.executable,
                EXPLAIN_PROGRAM_PATH,
                infile,
                quality_md,
                "--inifile",
                args.prompt_quality,
                *args_list,
            ],
        ):
            return
    else:
        print("** Warning: --prompt_quality が指定されていないため、quality生成をスキップします。")

    print("\n" + "=" * 60)
    print(" 全ての自動生成プロセスが正常に終了しました。")
    print("=" * 60)


def initialize():
    parser = argparse.ArgumentParser(
        description="Sphinxドキュメント生成の一連のルーチン（Docstring追加、バックアップ、解説生成、RST作成）を自動化します。"
    )

    parser.add_argument("infile", help="対象となるPythonスクリプトファイル名 (例: mu_fit.py)")
    parser.add_argument("--subdir", default=None, help="sourceディレクトリからの相対パス")

    parser.add_argument(
        "--prompt_explain",
        default="explain_program5.ini",
        help="usage生成用プロンプトファイルのパス",
    )
    parser.add_argument(
        "--prompt_quality",
        default="prompt_quality.ini",
        help="quality生成用プロンプトファイルのパス",
    )
    parser.add_argument(
        "--prompt_add_docstring",
        default="",
        help="add_docstring.pyに渡すプロンプトファイルのパス",
    )

    parser.add_argument("--api", choices=["openai", "openai5", "google", "gemini"], default="google")
    parser.add_argument("--update_index", type=int, default=0)
    parser.add_argument("-u", "--update", type=int, default=1)
    parser.add_argument("-w", "--overwrite", type=int, default=0)
    parser.add_argument("-p", "--pause", type=int, default=0)

    return parser


def read_args(parser):
    args = parser.parse_args()

    if args.subdir is None:
        args.subdir = relative_from_source(args.infile)

    return args


if __name__ == "__main__":
    print()
    print(f"=== {sys.argv[0]} ===")
    print(f"{EXPLAIN_PROGRAM_PATH=}")
    print(f"{ADD_DOCSTRING_PATH=}")

    parser = initialize()
#    args = read_args(parser)
    args = parser.parse_args()
    print()
    print("Args:")
    print(f"  {args.infile=}")
    print(f"  {args.subdir=}")
    print(f"  {args.api=}")
    print(f"  {args.update=}")
    print(f"  {args.overwrite=}")
    print(f"  {args.pause=}")
    print(f"  {args.prompt_explain=}")
    print(f"  {args.prompt_quality=}")
    print(f"  {args.prompt_add_docstring=}")
    print(f"    {args.update=}")
    print(f"    {args.overwrite=}")
    print(f"  {args.update_index=}")
    print(f"  {args.pause=}")

    try:
        main(args)
    except Exception:
        print("\n" + "!" * 60)
        print(" 予期せぬ致命的なエラーが発生しました。")
        traceback.print_exc()
        print("!" * 60)
        sys.exit(1)