"""
training_ppt.py
===============
Training module for PPT/PPTX presentation formats.

Handles text extraction from PowerPoint presentations (.ppt, .pptx),
semantic chunking, and FAISS embedding creation.

- .pptx: Extracted directly using python-pptx
- .ppt:  Converted to .pptx via LibreOffice, then extracted

Shared utilities (semantic chunking, embedding creation, FAISS merging)
are imported from training.py to maintain DRY principles.
"""

import os
import subprocess
import time

from pptx import Presentation
from langchain_core.documents import Document
from langchain_ollama import OllamaEmbeddings

# Import shared utilities from the existing training module
from training import (
    split_text_with_semantic_chunker,
    count_tokens_in_documents,
    save_documents_to_txt,
    create_and_save_embeddings,
    merge_all_faiss,
)


# ---------------------------------------------------------------------------
# Presentation Loading Helpers
# ---------------------------------------------------------------------------

def _extract_text_from_pptx(file_path: str) -> str:
    """
    Extract all text content from a .pptx file using python-pptx.

    Iterates through all slides and shapes to capture text from:
    - Text boxes and titles
    - Tables within slides
    - Grouped shapes (recursive extraction)
    - Notes sections
    """
    prs = Presentation(file_path)
    text_parts = []

    for slide_idx, slide in enumerate(prs.slides, start=1):
        slide_texts = []

        for shape in slide.shapes:
            # Extract from text frames (titles, body text, text boxes)
            if hasattr(shape, "text") and shape.text.strip():
                slide_texts.append(shape.text.strip())

            # Extract from tables
            if shape.has_table:
                for row in shape.table.rows:
                    for cell in row.cells:
                        cell_text = cell.text.strip()
                        if cell_text:
                            slide_texts.append(cell_text)

        # Extract from slide notes
        if slide.has_notes_slide and slide.notes_slide.notes_text_frame:
            notes_text = slide.notes_slide.notes_text_frame.text.strip()
            if notes_text:
                slide_texts.append(f"[Notes] {notes_text}")

        if slide_texts:
            # Add slide separator for semantic clarity
            text_parts.append(f"--- Slide {slide_idx} ---")
            text_parts.extend(slide_texts)

    return "\n".join(text_parts)


def _convert_ppt_to_pptx(ppt_file_path: str) -> str:
    """
    Convert a legacy .ppt file to .pptx using LibreOffice headless mode.

    Returns the path to the newly created .pptx file.
    Raises RuntimeError if LibreOffice conversion fails.
    """
    output_dir = os.path.dirname(ppt_file_path) or "."

    try:
        result = subprocess.run(
            [
                "libreoffice",
                "--headless",
                "--convert-to", "pptx",
                "--outdir", output_dir,
                ppt_file_path,
            ],
            capture_output=True,
            text=True,
            timeout=120,  # 2-minute timeout for large presentations
        )

        if result.returncode != 0:
            raise RuntimeError(
                f"LibreOffice conversion failed (exit code {result.returncode}): "
                f"{result.stderr}"
            )

    except FileNotFoundError:
        raise RuntimeError(
            "LibreOffice is not installed or not in PATH. "
            "It is required to convert legacy .ppt files."
        )

    # Derive the expected output path
    base_name = os.path.splitext(os.path.basename(ppt_file_path))[0]
    pptx_path = os.path.join(output_dir, f"{base_name}.pptx")

    if not os.path.exists(pptx_path):
        raise RuntimeError(
            f"LibreOffice conversion produced no output file. "
            f"Expected: {pptx_path}"
        )

    return pptx_path


# ---------------------------------------------------------------------------
# Main Presentation Loader
# ---------------------------------------------------------------------------

def load_ppt(file_path: str):
    """
    Load a PPT or PPTX file and return a list of LangChain Document objects.

    For .pptx files, text is extracted directly using python-pptx.
    For .ppt files, the presentation is first converted to .pptx via LibreOffice.

    Returns:
        list[Document]: List of LangChain Document objects with page_content set.
    """
    file_extension = os.path.splitext(file_path)[1].lower()
    converted_pptx_path = None

    try:
        if file_extension == ".pptx":
            text = _extract_text_from_pptx(file_path)
        elif file_extension == ".ppt":
            # Convert .ppt → .pptx first, then extract
            converted_pptx_path = _convert_ppt_to_pptx(file_path)
            text = _extract_text_from_pptx(converted_pptx_path)
        else:
            raise ValueError(
                f"Unsupported file type: {file_extension}. "
                f"Only .ppt and .pptx are supported."
            )

        if not text.strip():
            print(f"Warning: No text content extracted from {file_path}")
            return []

        # Wrap in LangChain Document objects (one document per presentation)
        documents = [
            Document(
                page_content=text,
                metadata={"source": file_path, "format": file_extension}
            )
        ]

        print(
            f"Successfully loaded {file_extension} presentation: "
            f"{len(text)} characters extracted."
        )
        return documents

    finally:
        # Clean up converted file if it was created
        if converted_pptx_path and os.path.exists(converted_pptx_path):
            os.remove(converted_pptx_path)
            print(f"Cleaned up temporary converted file: {converted_pptx_path}")


# ---------------------------------------------------------------------------
# Token Counting (compatible with Document objects from load_ppt)
# ---------------------------------------------------------------------------

def count_tokens_in_ppt_documents(documents):
    """
    Count total tokens across all Document objects.
    Uses whitespace tokenization for consistency with the existing system.
    """
    total_tokens = 0
    for doc in documents:
        tokens = doc.page_content.split()
        total_tokens += len(tokens)
    return total_tokens


# ---------------------------------------------------------------------------
# Main (for standalone testing)
# ---------------------------------------------------------------------------

def main():
    """Test the PPT/PPTX training pipeline standalone."""
    file_path = "test_presentation.pptx"  # Replace with actual test file
    client_id = "test_client"
    reference_id = "test_ref"
    output_dir = f"temp/{client_id}_{reference_id}"

    start_time = time.time()
    print(f"Start Time: {start_time}")

    # Step 1: Load the presentation
    docs = load_ppt(file_path)
    if not docs:
        print("No documents loaded. Exiting.")
        return

    token_count = count_tokens_in_ppt_documents(docs)
    print(f"Total tokens: {token_count}")

    # Step 2: Create embeddings and split
    embeddings = OllamaEmbeddings(model="nomic-embed-text")
    split_documents = split_text_with_semantic_chunker(docs, embeddings)

    # Step 3: Save text chunks
    save_documents_to_txt(split_documents, output_dir)

    # Step 4: Create and save embeddings
    create_and_save_embeddings(split_documents, client_id, reference_id)

    # Step 5: Merge FAISS indices
    merge_all_faiss(client_id, reference_id)

    end_time = time.time()
    print(f"Training process took {end_time - start_time:.2f} seconds")
    print(f"Total tokens processed: {token_count}")


if __name__ == "__main__":
    main()
