#!/usr/bin/env python3
"""Extract searchable text and media inventory from a PPTX file.

This intentionally uses only the Python standard library so it can run in
locked-down Codex workspaces where MarkItDown or python-pptx are unavailable.
"""

from __future__ import annotations

import argparse
import csv
import json
import re
import zipfile
from pathlib import Path
from xml.etree import ElementTree as ET


TEXT_NS = "{http://schemas.openxmlformats.org/drawingml/2006/main}"
SLIDE_RE = re.compile(r"ppt/slides/slide(\d+)\.xml$")


def iter_slide_names(zf: zipfile.ZipFile) -> list[str]:
    names = []
    for name in zf.namelist():
        match = SLIDE_RE.match(name)
        if match:
            names.append((int(match.group(1)), name))
    return [name for _, name in sorted(names)]


def clean_text(value: str) -> str:
    value = value.replace("\u000b", "\n")
    lines = [re.sub(r"\s+", " ", line).strip() for line in value.splitlines()]
    return "\n".join(line for line in lines if line)


def extract_slide_text(raw_xml: bytes) -> list[str]:
    root = ET.fromstring(raw_xml)
    paragraphs: list[str] = []
    for paragraph in root.iter(f"{TEXT_NS}p"):
        runs = []
        for text_node in paragraph.iter(f"{TEXT_NS}t"):
            if text_node.text:
                runs.append(text_node.text)
        text = clean_text("".join(runs))
        if text:
            paragraphs.append(text)
    return paragraphs


def title_from(paragraphs: list[str], slide_no: int) -> str:
    for text in paragraphs:
        text = text.strip()
        if text:
            return text[:80]
    return f"Slide {slide_no}"


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("pptx", type=Path)
    parser.add_argument("--out-dir", type=Path, required=True)
    args = parser.parse_args()

    args.out_dir.mkdir(parents=True, exist_ok=True)
    slides = []
    media = []

    with zipfile.ZipFile(args.pptx) as zf:
        for slide_no, name in enumerate(iter_slide_names(zf), start=1):
            paragraphs = extract_slide_text(zf.read(name))
            slides.append(
                {
                    "slide_no": slide_no,
                    "path": name,
                    "title": title_from(paragraphs, slide_no),
                    "paragraphs": paragraphs,
                    "char_count": sum(len(item) for item in paragraphs),
                }
            )

        for name in sorted(zf.namelist()):
            if name.startswith("ppt/media/"):
                info = zf.getinfo(name)
                media.append({"path": name, "size_bytes": info.file_size})

    json_path = args.out_dir / "slides.json"
    csv_path = args.out_dir / "slides.csv"
    md_path = args.out_dir / "slides.md"
    media_path = args.out_dir / "media-inventory.csv"

    json_path.write_text(json.dumps(slides, ensure_ascii=False, indent=2), encoding="utf-8")

    with csv_path.open("w", encoding="utf-8", newline="") as fh:
        writer = csv.DictWriter(
            fh, fieldnames=["slide_no", "path", "title", "char_count", "text_preview"]
        )
        writer.writeheader()
        for slide in slides:
            writer.writerow(
                {
                    "slide_no": slide["slide_no"],
                    "path": slide["path"],
                    "title": slide["title"],
                    "char_count": slide["char_count"],
                    "text_preview": " / ".join(slide["paragraphs"])[:500],
                }
            )

    md_lines = [
        "# PPTX Text Extraction",
        "",
        f"- source: `{args.pptx}`",
        f"- slide_count: {len(slides)}",
        f"- media_count: {len(media)}",
        "",
    ]
    for slide in slides:
        md_lines.append(f"## Slide {slide['slide_no']}: {slide['title']}")
        md_lines.append("")
        if slide["paragraphs"]:
            md_lines.extend(f"- {item}" for item in slide["paragraphs"])
        else:
            md_lines.append("- no text extracted")
        md_lines.append("")
    md_path.write_text("\n".join(md_lines), encoding="utf-8")

    with media_path.open("w", encoding="utf-8", newline="") as fh:
        writer = csv.DictWriter(fh, fieldnames=["path", "size_bytes"])
        writer.writeheader()
        writer.writerows(media)

    print(json.dumps({"slides": len(slides), "media": len(media)}, ensure_ascii=False))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
