#!/opt/homebrew/bin/python3.11
"""
Story Processor - AI-Driven Processor Contract Version 1.0

Compliant with AI-Driven Processor Contract v1.0
Effective Date: 2025-03-27

Features:
1. Standardized command-line interface
2. Redis progress reporting
3. Signal handling (SIGTERM, SIGINT)
4. Health check mode
5. Resource monitoring
6. Contract-compliant JSON output
7. Unified configuration
"""

import sys
import json
import os
import argparse
import signal
import time
import traceback
from datetime import datetime
from typing import Dict, Any, List

# Redis Publisher for progress reporting
try:
    sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
    from redis_publisher import RedisPublisher

    REDIS_AVAILABLE = True
except ImportError:
    REDIS_AVAILABLE = False
    print(
        "WARNING: RedisPublisher not available, progress reporting disabled",
        file=sys.stderr,
    )

# Contract version
CONTRACT_VERSION = "1.0"
PROCESSOR_NAME = (
    "/Users/accusys/momentry_core_0.1/scripts/story_processor_contract_v1.py"
)
PROCESSOR_VERSION = "1.0.0"
MODEL_NAME = "gpt-4"
MODEL_VERSION = "latest"

# Unified configuration defaults
DEFAULT_TIMEOUT = 3600  # 1 hour for story generation
DEFAULT_PARENT_CHUNK_SIZE = 5
DEFAULT_MIN_CHILD_CHUNKS = 3
DEFAULT_MAX_CHILD_CHUNKS = 10
DEFAULT_SUMMARY_LENGTH = 150
DEFAULT_MODEL = "openai"  # openai, local, or template
DEFAULT_MODEL_NAME = "gpt-4"
DEFAULT_TEMPERATURE = 0.7
DEFAULT_MAX_TOKENS = 500


# Signal handling with timeout support
class SignalHandler:
    """Handle system signals for graceful shutdown"""

    def __init__(self):
        self.should_exit = False
        self.exit_code = 0
        signal.signal(signal.SIGTERM, self.handle_signal)
        signal.signal(signal.SIGINT, self.handle_signal)

    def handle_signal(self, signum, frame):
        """Handle termination signals"""
        print(f"\n收到信号 {signum}，正在优雅关闭...")
        self.should_exit = True
        self.exit_code = 128 + signum

    def should_stop(self):
        """Check if should stop processing"""
        return self.should_exit


# Timeout manager
class TimeoutManager:
    """Manage processing timeouts"""

    def __init__(self, timeout_seconds: int):
        self.timeout_seconds = timeout_seconds
        self.start_time = time.time()
        self.timer = None

    def check_timeout(self) -> bool:
        """Check if timeout has been reached"""
        elapsed = time.time() - self.start_time
        return elapsed > self.timeout_seconds

    def get_remaining_time(self) -> float:
        """Get remaining time in seconds"""
        elapsed = time.time() - self.start_time
        return max(0, self.timeout_seconds - elapsed)

    def format_remaining_time(self) -> str:
        """Format remaining time as HH:MM:SS"""
        remaining = self.get_remaining_time()
        hours = int(remaining // 3600)
        minutes = int((remaining % 3600) // 60)
        seconds = int(remaining % 60)
        return f"{hours:02d}:{minutes:02d}:{seconds:02d}"


# Health check functions
def check_environment() -> Dict[str, Any]:
    """Check environment and dependencies"""
    checks = []

    # Check 1: OpenAI API (optional)
    try:
        import openai

        checks.append(
            {
                "name": "openai",
                "status": "available",
                "version": openai.__version__,
            }
        )
    except ImportError:
        checks.append({"name": "openai", "status": "optional", "version": None})

    # Check 2: Redis (optional)
    checks.append(
        {
            "name": "redis",
            "status": "available" if REDIS_AVAILABLE else "optional",
            "version": None,
        }
    )

    # Check 3: Python version
    checks.append(
        {
            "name": "python",
            "status": "available",
            "version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
        }
    )

    return {
        "timestamp": datetime.now().isoformat(),
        "processor_name": PROCESSOR_NAME,
        "processor_version": PROCESSOR_VERSION,
        "contract_version": CONTRACT_VERSION,
        "model_name": MODEL_NAME,
        "model_version": MODEL_VERSION,
        "checks": checks,
    }


def check_input_files(input_files: Dict[str, str]) -> Dict[str, Any]:
    """Check input files exist and are valid JSON"""
    results = {}

    for file_type, file_path in input_files.items():
        if not file_path:
            results[file_type] = {
                "exists": False,
                "valid": False,
                "error": "No path provided",
            }
            continue

        if not os.path.exists(file_path):
            results[file_type] = {
                "exists": False,
                "valid": False,
                "error": "File not found",
            }
            continue

        try:
            with open(file_path, "r") as f:
                data = json.load(f)

            # Basic validation based on file type
            if file_type == "asr":
                valid = isinstance(data, dict) and "segments" in data
            elif file_type == "cut":
                valid = isinstance(data, dict) and "scenes" in data
            elif file_type == "yolo":
                valid = isinstance(data, dict) and "detections" in data
            elif file_type == "ocr":
                valid = isinstance(data, dict) and "texts" in data
            else:
                valid = isinstance(data, dict)

            results[file_type] = {
                "exists": True,
                "valid": valid,
                "size": os.path.getsize(file_path),
                "data_keys": list(data.keys()) if isinstance(data, dict) else [],
            }

        except json.JSONDecodeError as e:
            results[file_type] = {
                "exists": True,
                "valid": False,
                "error": f"Invalid JSON: {e}",
            }
        except Exception as e:
            results[file_type] = {"exists": True, "valid": False, "error": str(e)}

    return results


def load_input_data(input_files: Dict[str, str]) -> Dict[str, Any]:
    """Load input data from JSON files"""
    data = {}

    for file_type, file_path in input_files.items():
        if not file_path or not os.path.exists(file_path):
            data[file_type] = None
            continue

        try:
            with open(file_path, "r") as f:
                data[file_type] = json.load(f)
        except:
            data[file_type] = None

    return data


def generate_parent_child_chunks(
    asr_data: Dict,
    cut_data: Dict,
    yolo_data: Dict,
    ocr_data: Dict,
    parent_chunk_size: int = DEFAULT_PARENT_CHUNK_SIZE,
    min_child_chunks: int = DEFAULT_MIN_CHILD_CHUNKS,
    max_child_chunks: int = DEFAULT_MAX_CHILD_CHUNKS,
    summary_length: int = DEFAULT_SUMMARY_LENGTH,
    model: str = DEFAULT_MODEL,
    **kwargs,
) -> List[Dict[str, Any]]:
    """Generate parent-child chunk hierarchy for RAG"""

    parent_chunks = []

    # Extract ASR segments
    asr_segments = asr_data.get("segments", []) if asr_data else []

    # Extract scenes from CUT data
    scenes = cut_data.get("scenes", []) if cut_data else []

    # Extract detections from YOLO data
    yolo_detections = yolo_data.get("detections", []) if yolo_data else []

    # Extract OCR texts
    ocr_texts = ocr_data.get("texts", []) if ocr_data else []

    # If we have scenes, use them to group content
    if scenes:
        for scene in scenes:
            scene_start = scene.get("start_time", 0)
            scene_end = scene.get("end_time", 0)
            scene_duration = scene.get("duration", 0)

            # Find ASR segments in this scene
            scene_asr_segments = []
            for segment in asr_segments:
                seg_start = segment.get("start", 0)
                if scene_start <= seg_start <= scene_end:
                    scene_asr_segments.append(segment)

            # Find YOLO detections in this scene
            scene_yolo_detections = []
            for detection in yolo_detections:
                det_time = detection.get("timestamp", 0)
                if scene_start <= det_time <= scene_end:
                    scene_yolo_detections.append(detection)

            # Find OCR texts in this scene
            scene_ocr_texts = []
            for text in ocr_texts:
                text_time = text.get("timestamp", 0)
                if scene_start <= text_time <= scene_end:
                    scene_ocr_texts.append(text)

            # Create child chunks
            child_chunks = []

            # Add ASR segments as child chunks
            for segment in scene_asr_segments[:max_child_chunks]:
                child_chunks.append(
                    {
                        "type": "asr",
                        "content": segment.get("text", ""),
                        "start_time": segment.get("start", 0),
                        "end_time": segment.get("end", 0),
                        "confidence": segment.get("confidence", 0),
                        "metadata": {"speaker": segment.get("speaker")},
                    }
                )

            # Add YOLO detections as child chunks
            for detection in scene_yolo_detections[:max_child_chunks]:
                child_chunks.append(
                    {
                        "type": "yolo",
                        "content": f"Detected {detection.get('class', 'object')} with confidence {detection.get('confidence', 0):.2f}",
                        "timestamp": detection.get("timestamp", 0),
                        "confidence": detection.get("confidence", 0),
                        "metadata": {
                            "class": detection.get("class"),
                            "bbox": detection.get("bbox"),
                        },
                    }
                )

            # Add OCR texts as child chunks
            for text in scene_ocr_texts[:max_child_chunks]:
                child_chunks.append(
                    {
                        "type": "ocr",
                        "content": text.get("text", ""),
                        "timestamp": text.get("timestamp", 0),
                        "confidence": text.get("confidence", 0),
                        "metadata": {
                            "bbox": text.get("bbox"),
                            "language": text.get("language"),
                        },
                    }
                )

            # Skip if not enough child chunks
            if len(child_chunks) < min_child_chunks:
                continue

            # Generate parent summary
            if model == "openai":
                parent_summary = generate_openai_summary(child_chunks, scene, **kwargs)
            elif model == "local":
                parent_summary = generate_local_summary(child_chunks, scene, **kwargs)
            else:
                parent_summary = generate_template_summary(child_chunks, scene)

            # Create parent chunk
            parent_chunks.append(
                {
                    "parent_id": len(parent_chunks) + 1,
                    "scene_id": scene.get("scene_id", 0),
                    "start_time": scene_start,
                    "end_time": scene_end,
                    "duration": scene_duration,
                    "summary": parent_summary[:summary_length]
                    if summary_length > 0
                    else parent_summary,
                    "child_count": len(child_chunks),
                    "child_types": list(set(chunk["type"] for chunk in child_chunks)),
                    "child_chunks": child_chunks[
                        :parent_chunk_size
                    ],  # Limit child chunks in output
                }
            )

    # If no scenes, create chunks based on time windows
    elif asr_segments:
        # Group ASR segments by time windows
        time_window = 30  # seconds
        current_window = 0

        while current_window * time_window < (
            asr_segments[-1].get("end", 0) if asr_segments else 0
        ):
            window_start = current_window * time_window
            window_end = (current_window + 1) * time_window

            # Find segments in this window
            window_segments = []
            for segment in asr_segments:
                seg_start = segment.get("start", 0)
                if window_start <= seg_start < window_end:
                    window_segments.append(segment)

            if len(window_segments) >= min_child_chunks:
                # Create child chunks
                child_chunks = []
                for segment in window_segments[:max_child_chunks]:
                    child_chunks.append(
                        {
                            "type": "asr",
                            "content": segment.get("text", ""),
                            "start_time": segment.get("start", 0),
                            "end_time": segment.get("end", 0),
                            "confidence": segment.get("confidence", 0),
                            "metadata": {"speaker": segment.get("speaker")},
                        }
                    )

                # Generate parent summary
                parent_summary = generate_template_summary(
                    child_chunks,
                    {
                        "start_time": window_start,
                        "end_time": window_end,
                        "duration": time_window,
                    },
                )

                # Create parent chunk
                parent_chunks.append(
                    {
                        "parent_id": len(parent_chunks) + 1,
                        "time_window": current_window,
                        "start_time": window_start,
                        "end_time": window_end,
                        "duration": time_window,
                        "summary": parent_summary[:summary_length]
                        if summary_length > 0
                        else parent_summary,
                        "child_count": len(child_chunks),
                        "child_types": ["asr"],
                        "child_chunks": child_chunks[:parent_chunk_size],
                    }
                )

            current_window += 1

    return parent_chunks


def generate_openai_summary(child_chunks: List[Dict], scene: Dict, **kwargs) -> str:
    """Generate summary using OpenAI"""
    try:
        import openai

        # Prepare context from child chunks
        context_parts = []
        for chunk in child_chunks[:10]:  # Limit context size
            if chunk["type"] == "asr":
                context_parts.append(f"Speech: {chunk['content']}")
            elif chunk["type"] == "yolo":
                context_parts.append(f"Visual: {chunk['content']}")
            elif chunk["type"] == "ocr":
                context_parts.append(f"Text: {chunk['content']}")

        context = "\n".join(context_parts)

        # Prepare prompt
        prompt = f"""Summarize this video scene ({scene.get("duration", 0):.1f} seconds) based on the following elements:

{context}

Provide a concise narrative summary that connects the speech, visual elements, and text into a coherent description."""

        # Call OpenAI API
        response = openai.chat.completions.create(
            model=kwargs.get("model_name", DEFAULT_MODEL_NAME),
            messages=[
                {
                    "role": "system",
                    "content": "You are a video analysis assistant that creates coherent narrative summaries from multiple data sources.",
                },
                {"role": "user", "content": prompt},
            ],
            max_tokens=kwargs.get("max_tokens", DEFAULT_MAX_TOKENS),
            temperature=kwargs.get("temperature", DEFAULT_TEMPERATURE),
        )

        return response.choices[0].message.content

    except ImportError:
        return "OpenAI not available for summary generation"
    except Exception as e:
        return f"Summary generation error: {str(e)}"


def generate_local_summary(child_chunks: List[Dict], scene: Dict, **kwargs) -> str:
    """Generate summary using local model (placeholder)"""
    # This is a placeholder for local model implementation
    asr_count = sum(1 for chunk in child_chunks if chunk["type"] == "asr")
    yolo_count = sum(1 for chunk in child_chunks if chunk["type"] == "yolo")
    ocr_count = sum(1 for chunk in child_chunks if chunk["type"] == "ocr")

    return f"Scene ({scene.get('duration', 0):.1f}s) with {asr_count} speech segments, {yolo_count} visual detections, and {ocr_count} text elements. Local summary model not implemented."


def generate_template_summary(child_chunks: List[Dict], scene: Dict) -> str:
    """Generate summary using template"""
    asr_count = sum(1 for chunk in child_chunks if chunk["type"] == "asr")
    yolo_count = sum(1 for chunk in child_chunks if chunk["type"] == "yolo")
    ocr_count = sum(1 for chunk in child_chunks if chunk["type"] == "ocr")

    # Extract some sample content
    asr_samples = [
        chunk["content"][:50] for chunk in child_chunks if chunk["type"] == "asr"
    ][:2]
    yolo_classes = list(
        set(
            chunk["metadata"].get("class", "object")
            for chunk in child_chunks
            if chunk["type"] == "yolo"
        )
    )

    summary_parts = [f"Scene duration: {scene.get('duration', 0):.1f} seconds."]

    if asr_count > 0:
        summary_parts.append(f"Contains {asr_count} speech segments.")
        if asr_samples:
            summary_parts.append(f"Sample speech: {'; '.join(asr_samples)}...")

    if yolo_count > 0:
        summary_parts.append(
            f"Detected {yolo_count} objects including: {', '.join(yolo_classes[:3])}."
        )

    if ocr_count > 0:
        summary_parts.append(f"Extracted {ocr_count} text elements from the video.")

    return " ".join(summary_parts)


# Main processing function
def process_story(
    asr_path: str,
    cut_path: str,
    yolo_path: str,
    ocr_path: str,
    output_path: str,
    uuid: str = "",
    parent_chunk_size: int = DEFAULT_PARENT_CHUNK_SIZE,
    min_child_chunks: int = DEFAULT_MIN_CHILD_CHUNKS,
    max_child_chunks: int = DEFAULT_MAX_CHILD_CHUNKS,
    summary_length: int = DEFAULT_SUMMARY_LENGTH,
    model: str = DEFAULT_MODEL,
    model_name: str = DEFAULT_MODEL_NAME,
    temperature: float = DEFAULT_TEMPERATURE,
    max_tokens: int = DEFAULT_MAX_TOKENS,
    timeout: int = DEFAULT_TIMEOUT,
) -> Dict[str, Any]:
    """Process video analysis data to create parent-child chunk hierarchy"""

    # Initialize
    signal_handler = SignalHandler()
    timeout_manager = TimeoutManager(timeout)
    publisher = None
    if REDIS_AVAILABLE and uuid:
        try:
            publisher = RedisPublisher(uuid)
        except:
            publisher = None

    def publish(stage: str, message: str, data: Dict = None):
        if publisher:
            publisher.info(PROCESSOR_NAME, stage, message, data)

    if publisher:
        publish("STORY_START", "开始生成故事层次结构")

    result = {
        "processor_name": PROCESSOR_NAME,
        "processor_version": PROCESSOR_VERSION,
        "contract_version": CONTRACT_VERSION,
        "model_name": MODEL_NAME,
        "model_version": MODEL_VERSION,
        "input_files": {
            "asr": asr_path,
            "cut": cut_path,
            "yolo": yolo_path,
            "ocr": ocr_path,
        },
        "output_path": output_path,
        "uuid": uuid,
        "timestamp": datetime.now().isoformat(),
        "parameters": {
            "parent_chunk_size": parent_chunk_size,
            "min_child_chunks": min_child_chunks,
            "max_child_chunks": max_child_chunks,
            "summary_length": summary_length,
            "model": model,
            "model_name": model_name,
            "temperature": temperature,
            "max_tokens": max_tokens,
            "timeout": timeout,
        },
        "success": False,
        "error": None,
        "parent_chunks": [],
        "chunk_statistics": {},
        "processing_time": 0,
        "resource_usage": {},
    }

    start_time = time.time()

    try:
        # Check timeout
        if timeout_manager.check_timeout():
            raise TimeoutError(f"超时 ({timeout} 秒)")

        # Check if should exit
        if signal_handler.should_stop():
            raise KeyboardInterrupt("收到停止信号")

        # Check input files
        if publisher:
            publish("STORY_CHECK_FILES", "检查输入文件")

        input_files = {
            "asr": asr_path,
            "cut": cut_path,
            "yolo": yolo_path,
            "ocr": ocr_path,
        }

        file_checks = check_input_files(input_files)
        result["file_checks"] = file_checks

        # Check if we have at least ASR data
        if not file_checks.get("asr", {}).get("valid", False):
            raise ValueError("缺少有效的 ASR 数据文件")

        if publisher:
            publish("STORY_FILES_VALID", "输入文件检查通过")

        # Load input data
        if publisher:
            publish("STORY_LOAD_DATA", "加载输入数据")

        input_data = load_input_data(input_files)

        if publisher:
            publish("STORY_DATA_LOADED", "数据加载完成")

        # Generate parent-child chunks
        if publisher:
            publish("STORY_GENERATE_CHUNKS", "生成父-子块层次结构")

        parent_chunks = generate_parent_child_chunks(
            asr_data=input_data.get("asr"),
            cut_data=input_data.get("cut"),
            yolo_data=input_data.get("yolo"),
            ocr_data=input_data.get("ocr"),
            parent_chunk_size=parent_chunk_size,
            min_child_chunks=min_child_chunks,
            max_child_chunks=max_child_chunks,
            summary_length=summary_length,
            model=model,
            model_name=model_name,
            temperature=temperature,
            max_tokens=max_tokens,
        )

        result["parent_chunks"] = parent_chunks
        result["parent_chunk_count"] = len(parent_chunks)

        # Calculate statistics
        total_child_chunks = sum(chunk.get("child_count", 0) for chunk in parent_chunks)
        child_types = {}
        for chunk in parent_chunks:
            for child_type in chunk.get("child_types", []):
                child_types[child_type] = child_types.get(child_type, 0) + 1

        result["chunk_statistics"] = {
            "total_parent_chunks": len(parent_chunks),
            "total_child_chunks": total_child_chunks,
            "avg_children_per_parent": total_child_chunks / len(parent_chunks)
            if parent_chunks
            else 0,
            "child_type_distribution": child_types,
        }

        result["success"] = True

        if publisher:
            publish("STORY_COMPLETE", f"完成: {len(parent_chunks)} 个父块")

    except TimeoutError as e:
        result["error"] = f"处理超时: {e}"
        if publisher:
            publish("STORY_TIMEOUT", f"超时: {e}")
    except KeyboardInterrupt:
        result["error"] = "处理被用户中断"
        if publisher:
            publish("STORY_INTERRUPTED", "处理被中断")
    except ImportError as e:
        result["error"] = f"依赖缺失: {e}"
        if publisher:
            publish("STORY_MISSING_DEPS", f"缺少依赖: {e}")
    except Exception as e:
        result["error"] = f"处理错误: {str(e)}"
        if publisher:
            publish("STORY_ERROR", f"错误: {str(e)}")
        traceback.print_exc()

    # Calculate processing time
    processing_time = time.time() - start_time
    result["processing_time"] = processing_time

    # Add resource usage
    try:
        import psutil

        process = psutil.Process()
        memory_info = process.memory_info()
        result["resource_usage"] = {
            "cpu_percent": process.cpu_percent(),
            "memory_mb": memory_info.rss / (1024 * 1024),
            "user_time": process.cpu_times().user,
            "system_time": process.cpu_times().system,
        }
    except ImportError:
        result["resource_usage"] = {"error": "psutil not available"}

    # Save result
    try:
        with open(output_path, "w") as f:
            json.dump(result, f, indent=2, ensure_ascii=False)
        if publisher:
            publish("STORY_SAVED", f"结果保存到: {output_path}")
    except Exception as e:
        result["error"] = f"保存结果失败: {str(e)}"
        if publisher:
            publish("STORY_SAVE_ERROR", f"保存失败: {str(e)}")

    return result


def main():
    """Main entry point"""
    parser = argparse.ArgumentParser(
        description=f"{PROCESSOR_NAME.upper()} Processor v{PROCESSOR_VERSION} - Parent-Child Chunk Generation"
    )
    parser.add_argument("--asr", help="Path to ASR JSON file", required=True)
    parser.add_argument("--cut", help="Path to CUT JSON file", default="")
    parser.add_argument("--yolo", help="Path to YOLO JSON file", default="")
    parser.add_argument("--ocr", help="Path to OCR JSON file", default="")
    parser.add_argument("--output", help="Path to output JSON file", required=True)
    parser.add_argument("--uuid", help="UUID for progress tracking", default="")
    parser.add_argument(
        "--parent-chunk-size",
        help=f"Maximum child chunks per parent (default: {DEFAULT_PARENT_CHUNK_SIZE})",
        type=int,
        default=DEFAULT_PARENT_CHUNK_SIZE,
    )
    parser.add_argument(
        "--min-child-chunks",
        help=f"Minimum child chunks to create parent (default: {DEFAULT_MIN_CHILD_CHUNKS})",
        type=int,
        default=DEFAULT_MIN_CHILD_CHUNKS,
    )
    parser.add_argument(
        "--max-child-chunks",
        help=f"Maximum child chunks per parent (default: {DEFAULT_MAX_CHILD_CHUNKS})",
        type=int,
        default=DEFAULT_MAX_CHILD_CHUNKS,
    )
    parser.add_argument(
        "--summary-length",
        help=f"Maximum summary length in characters (default: {DEFAULT_SUMMARY_LENGTH})",
        type=int,
        default=DEFAULT_SUMMARY_LENGTH,
    )
    parser.add_argument(
        "--model",
        help=f"Summary model to use (default: {DEFAULT_MODEL})",
        default=DEFAULT_MODEL,
        choices=["openai", "local", "template"],
    )
    parser.add_argument(
        "--model-name",
        help=f"Model name for OpenAI (default: {DEFAULT_MODEL_NAME})",
        default=DEFAULT_MODEL_NAME,
    )
    parser.add_argument(
        "--temperature",
        help=f"Temperature for generation (default: {DEFAULT_TEMPERATURE})",
        type=float,
        default=DEFAULT_TEMPERATURE,
    )
    parser.add_argument(
        "--max-tokens",
        help=f"Maximum tokens per summary (default: {DEFAULT_MAX_TOKENS})",
        type=int,
        default=DEFAULT_MAX_TOKENS,
    )
    parser.add_argument(
        "--timeout",
        help=f"Timeout in seconds (default: {DEFAULT_TIMEOUT})",
        type=int,
        default=DEFAULT_TIMEOUT,
    )
    parser.add_argument(
        "--health-check",
        help="Run health check and exit",
        action="store_true",
    )

    args = parser.parse_args()

    # Health check mode
    if args.health_check:
        health = check_environment()
        print(json.dumps(health, indent=2, ensure_ascii=False))
        return (
            0
            if all(c["status"] in ["available", "optional"] for c in health["checks"])
            else 1
        )

    # Normal processing mode
    result = process_story(
        asr_path=args.asr,
        cut_path=args.cut,
        yolo_path=args.yolo,
        ocr_path=args.ocr,
        output_path=args.output,
        uuid=args.uuid,
        parent_chunk_size=args.parent_chunk_size,
        min_child_chunks=args.min_child_chunks,
        max_child_chunks=args.max_child_chunks,
        summary_length=args.summary_length,
        model=args.model,
        model_name=args.model_name,
        temperature=args.temperature,
        max_tokens=args.max_tokens,
        timeout=args.timeout,
    )

    # Print result summary
    if result.get("success", False):
        print(f"✅ {PROCESSOR_NAME.upper()} 处理成功")
        print(f"   父块数: {result.get('parent_chunk_count', 0)}")
        stats = result.get("chunk_statistics", {})
        print(f"   子块总数: {stats.get('total_child_chunks', 0)}")
        print(f"   平均子块/父块: {stats.get('avg_children_per_parent', 0):.1f}")
        print(f"   处理时间: {result.get('processing_time', 0):.1f} 秒")
        print(f"   输出文件: {args.output}")
        return 0
    else:
        print(f"❌ {PROCESSOR_NAME.upper()} 处理失败")
        print(f"   错误: {result.get('error', '未知错误')}")
        return 1


if __name__ == "__main__":
    sys.exit(main())
