"""
1. DB作成
cd your_project_folder
python app.py --update "D:\doc1.current\doc.OHP集" -1 paragraph
2. Webサーバ起動cd your_project_folder
python app.py
3.アクセス
http://127.0.0.1:5000/
"""

import os
import sys
import sqlite3
from pptx import Presentation
from flask import Flask, render_template, jsonify, request
from html import escape # HTMLエスケープ処理用
import urllib.parse # URLエンコード/デコード用


# Flaskアプリケーションの初期化
app = Flask(__name__)

# SQLiteデータベースファイルのパス
# このファイルはapp.pyと同じディレクトリに作成されます
SQLITE_DB_NAME = "slide_db.sqlite3"

# --- PowerPointテキスト抽出関数 ---
# この関数はDBに保存するための生テキストを返す
def extract_text_from_pptx_slides(pptx_path, mode="paragraph"):
    """
    指定されたPowerPointファイルから、各スライドのテキストをリストで抽出する。
    スライドタイトルがある場合は、それを優先して抽出する。
    引数:
        pptx_path (str): PowerPointファイルのフルパス。
        mode (str): テキストの抽出単位。"paragraph" (デフォルト) または "run"。
    戻り値:
        list: 各スライドのデータを含む辞書のリスト。
              各辞書は "slide_number", "title", "first_line", "full_text" を含む。
    """
    slides_data = []
    try:
        prs = Presentation(pptx_path)
        for i, slide in enumerate(prs.slides):
            slide_title_text = ""
            if slide.shapes.title:
                slide_title_text = slide.shapes.title.text_frame.text.strip()

            current_slide_texts = []
            if slide_title_text:
                current_slide_texts.append(slide_title_text)

            for shape in slide.shapes:
                if shape == slide.shapes.title:
                    continue

                if hasattr(shape, "text_frame") and shape.text_frame:
                    if mode == "paragraph":
                        for paragraph in shape.text_frame.paragraphs:
                            para_runs_text = []
                            for run in paragraph.runs:
                                if run.text:
                                    para_runs_text.append(run.text.strip())
                            if para_runs_text:
                                current_slide_texts.append(" ".join(para_runs_text).strip())
                    elif mode == "run":
                        for paragraph in shape.text_frame.paragraphs:
                            for run in paragraph.runs:
                                if run.text:
                                    current_slide_texts.append(run.text.strip())
                    else:
                        print(f"Warning: Unknown extract_mode '{mode}'. Defaulting to 'paragraph' mode for {pptx_path}.")
                        for paragraph in shape.text_frame.paragraphs:
                            para_runs_text = []
                            for run in paragraph.runs:
                                if run.text:
                                    para_runs_text.append(run.text.strip())
                            if para_runs_text:
                                current_slide_texts.append(" ".join(para_runs_text).strip())

            full_text = "\n".join(current_slide_texts).strip()
            first_line_for_summary = slide_title_text if slide_title_text else (current_slide_texts[0] if current_slide_texts else "(No text found)")
            
            slides_data.append({
                "slide_number": i + 1,
                "title": slide_title_text,
                "first_line": first_line_for_summary,
                "full_text": full_text
            })

    except Exception as e:
        print(f"Error processing {pptx_path}: {e}")
        slides_data.append({
            "slide_number": "Error",
            "title": "Extraction Error",
            "first_line": f"Error extracting text: {escape(str(e))}", # エラーメッセージはHTMLエスケープ
            "full_text": f"Error extracting text from this slide: {escape(str(e))}" # エラーメッセージはHTMLエスケープ
        })
    return slides_data

# --- SQLiteデータベース操作関数 ---
def init_db():
    """データベーステーブルを初期化する"""
    conn = None
    try:
        conn = sqlite3.connect(SQLITE_DB_NAME)
        cursor = conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS slides (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                pptx_path TEXT NOT NULL,
                slide_number INTEGER NOT NULL,
                title TEXT,
                first_line TEXT,
                full_text TEXT
            )
        """)
        conn.commit()
        print(f"Database '{SQLITE_DB_NAME}' initialized.")
    except sqlite3.Error as e:
        print(f"SQLite error during DB initialization: {e}")
    finally:
        if conn:
            conn.close()

def store_data_in_sqlite(db_path, all_pptx_data):
    """
    抽出したPowerPointデータをSQLiteデータベースに保存する。
    """
    conn = None
    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()

        # 既存のデータをクリア
        cursor.execute("DELETE FROM slides")
        conn.commit()

        # 新しいデータを挿入
        for pptx_path, slides in all_pptx_data.items():
            for slide_data in slides:
                cursor.execute("""
                    INSERT INTO slides (pptx_path, slide_number, title, first_line, full_text)
                    VALUES (?, ?, ?, ?, ?)
                """, (
                    pptx_path,
                    slide_data["slide_number"],
                    slide_data["title"],
                    slide_data["first_line"],
                    slide_data["full_text"]
                ))
        conn.commit()
        print(f"Data successfully stored in SQLite database: {db_path}")
    except sqlite3.Error as e:
        print(f"SQLite error when storing data: {e}")
    finally:
        if conn:
            conn.close()

def get_all_slides_summary_from_db():
    """
    SQLiteデータベースからすべてのスライドの概要データを読み込む。
    フルテキストは含まない。APIエンドポイント用。
    """
    conn = None
    slides_summary_list = []
    try:
        conn = sqlite3.connect(SQLITE_DB_NAME)
        cursor = conn.cursor()
        # フルテキスト以外のカラムを取得
        cursor.execute("SELECT pptx_path, slide_number, title, first_line FROM slides ORDER BY pptx_path, slide_number")
        rows = cursor.fetchall()
        
        for row in rows:
            pptx_path, slide_number, title, first_line = row
            slides_summary_list.append({
                "pptx_path": pptx_path,
                "slide_number": slide_number,
                "title": title if title else "",
                "first_line": first_line
            })
    except sqlite3.Error as e:
        print(f"SQLite error when reading summary data for API: {e}")
    finally:
        if conn:
            conn.close()
    return slides_summary_list

def get_single_slide_full_text_from_db(pptx_path, slide_number):
    """
    指定されたPowerPointパスとスライド番号に対応するフルテキストをデータベースから取得する。
    """
    conn = None
    full_text = None
    try:
        conn = sqlite3.connect(SQLITE_DB_NAME)
        cursor = conn.cursor()
        cursor.execute("""
            SELECT full_text FROM slides
            WHERE pptx_path = ? AND slide_number = ?
        """, (pptx_path, slide_number))
        result = cursor.fetchone()
        if result:
            full_text = result[0]
    except sqlite3.Error as e:
        print(f"SQLite error when reading single slide full text: {e}")
    finally:
        if conn:
            conn.close()
    return full_text

def search_slides_in_db(search_term):
    """
    データベース内で検索語に一致するスライドを検索し、そのデータを返す。
    """
    conn = None
    results = []
    try:
        conn = sqlite3.connect(SQLITE_DB_NAME)
        cursor = conn.cursor()
        # %をワイルドカードとして使用するため、検索語内の%をエスケープ
        # また、SQLiteのLIKEはデフォルトで大文字小文字を区別しない（ASCIIの場合）
        # 日本語の場合はCOLLATE NOCASEを使うか、lower()で統一する
        # ここでは単純にlower()で統一する
        search_term_lower = f"%{search_term.lower()}%"
        
        cursor.execute("""
            SELECT pptx_path, slide_number, title, first_line, full_text
            FROM slides
            WHERE LOWER(title) LIKE ? OR LOWER(first_line) LIKE ? OR LOWER(full_text) LIKE ?
            ORDER BY pptx_path, slide_number
        """, (search_term_lower, search_term_lower, search_term_lower))
        
        rows = cursor.fetchall()
        for row in rows:
            pptx_path, slide_number, title, first_line, full_text = row
            results.append({
                "pptx_path": pptx_path,
                "slide_number": slide_number,
                "title": title if title else "",
                "first_line": first_line,
                "full_text": full_text # 検索結果にはフルテキストを含める
            })
    except sqlite3.Error as e:
        print(f"SQLite error during search: {e}")
    finally:
        if conn:
            conn.close()
    return results

# --- Flask ルート定義 ---

@app.route('/')
def index():
    """メインHTMLページを提供する"""
    return render_template('index.html')

@app.route('/api/slides')
def api_slides_summary():
    """
    すべてのスライドの概要データをJSON形式で提供するAPIエンドポイント。
    """
    slides = get_all_slides_summary_from_db()
    return jsonify(slides)

@app.route('/api/slide/<path:encoded_pptx_path>/<int:slide_number>')
def api_single_slide_full_text(encoded_pptx_path, slide_number):
    """
    特定のPowerPointスライドのフルテキストをJSON形式で提供するAPIエンドポイント。
    pptx_pathはURLエンコードされているためデコードが必要。
    """
    pptx_path = urllib.parse.unquote(encoded_pptx_path) # URLデコード
    full_text = get_single_slide_full_text_from_db(pptx_path, slide_number)
    if full_text is not None:
        return jsonify({"pptx_path": pptx_path, "slide_number": slide_number, "full_text": full_text})
    else:
        return jsonify({"error": "Slide not found or text not available"}), 404

@app.route('/api/search_slides')
def api_search_slides():
    """
    検索語に基づいてスライドを検索し、結果をJSON形式で提供するAPIエンドポイント。
    """
    search_term = request.args.get('term', '')
    if not search_term:
        return jsonify({"results": []})
    
    results = search_slides_in_db(search_term)
    return jsonify(results)

@app.route('/update_db')
def update_db():
    """
    PowerPointファイルを検索し、データベースを更新するエンドポイント。
    ブラウザからアクセスして手動でDB更新をトリガーできます。
    例: http://localhost:5000/update_db?root_dir=C:\path\to\pptx&max_level=-1&extract_mode=paragraph
    """
    root_dir = request.args.get('root_dir', '.')
    max_level_str = request.args.get('max_level', '-1')
    extract_mode = request.args.get('extract_mode', 'paragraph')

    try:
        max_level = int(max_level_str)
    except ValueError:
        return jsonify({"status": "error", "message": "Invalid max_level"}), 400

    if extract_mode not in ["run", "paragraph"]:
        return jsonify({"status": "error", "message": "Invalid extract_mode"}), 400

    if not os.path.isdir(root_dir):
        return jsonify({"status": "error", "message": f"Root directory '{root_dir}' does not exist."}), 400

    root_dir_abs = os.path.abspath(root_dir)
    print(f"Updating DB: Searching for .pptx files in '{root_dir_abs}' (max_level: {max_level})...")

    all_extracted_pptx_data = {}
    file_count = 0
    for dirpath, dirnames, filenames in os.walk(root_dir_abs):
        current_level = dirpath.count(os.sep) - root_dir_abs.count(os.sep)
        
        if max_level != -1 and current_level >= max_level:
            del dirnames[:]
            continue

        for filename in filenames:
            if filename.lower().endswith(".pptx"):
                pptx_full_path = os.path.join(dirpath, filename)
                file_count += 1
                print(f"  Extracting text from: {pptx_full_path}")
                slides_data = extract_text_from_pptx_slides(pptx_full_path, mode=extract_mode)
                all_extracted_pptx_data[pptx_full_path] = slides_data
    
    if file_count == 0:
        print("No .pptx files found to process for DB update.")
    
    store_data_in_sqlite(SQLITE_DB_NAME, all_extracted_pptx_data)
    
    return jsonify({"status": "success", "message": f"Database updated with {file_count} .pptx files.", "root_dir": root_dir, "max_level": max_level, "extract_mode": extract_mode})


# --- アプリケーション起動 ---
if __name__ == '__main__':
    # データベースを初期化（テーブル作成など）
    init_db()
    
    # コマンドライン引数で初期のDB更新をトリガーすることも可能
    # 例: python app.py --update "C:\path\to\pptx" -1 paragraph
    if "--update" in sys.argv:
        try:
            update_index = sys.argv.index("--update")
            root_dir_arg = sys.argv[update_index + 1] if len(sys.argv) > update_index + 1 else "."
            max_level_arg = sys.argv[update_index + 2] if len(sys.argv) > update_index + 2 else "-1"
            extract_mode_arg = sys.argv[update_index + 3] if len(sys.argv) > update_index + 3 else "paragraph"

            max_level_val = int(max_level_arg)
            if extract_mode_arg not in ["run", "paragraph"]:
                extract_mode_arg = "paragraph"

            print("\n--- Initial Database Update Triggered ---")
            root_dir_abs = os.path.abspath(root_dir_arg)
            all_extracted_pptx_data = {}
            file_count = 0
            for dirpath, dirnames, filenames in os.walk(root_dir_abs):
                current_level = dirpath.count(os.sep) - root_dir_abs.count(os.sep)
                if max_level_val != -1 and current_level >= max_level_val:
                    del dirnames[:]
                    continue
                for filename in filenames:
                    if filename.lower().endswith(".pptx"):
                        pptx_full_path = os.path.join(dirpath, filename)
                        file_count += 1
                        print(f"  Extracting text from: {pptx_full_path}")
                        slides_data = extract_text_from_pptx_slides(pptx_full_path, mode=extract_mode_arg)
                        all_extracted_pptx_data[pptx_full_path] = slides_data
            store_data_in_sqlite(SQLITE_DB_NAME, all_extracted_pptx_data)
            print(f"--- Initial DB Update Complete: {file_count} files processed ---\n")

        except (ValueError, IndexError):
            print("Usage for initial update: python app.py --update [root_dir] [max_level] [extract_mode]")
            print("Example: python app.py --update . -1 paragraph")
            sys.exit(1)


    # Flaskサーバーを起動
    app.run(debug=True) # debug=Trueは開発用です。本番環境ではFalseにしてください。