"""
Extract {Physics Name} from FEA results.

This is a template for creating new physics extractors.
Copy this file to optimization_engine/extractors/extract_{physics}.py
and customize for your specific physics extraction.

Author: {Your Name}
Created: {Date}
Version: 1.0
"""

from pathlib import Path
from typing import Dict, Any, Optional, Union
from pyNastran.op2.op2 import OP2


def extract_{physics}(
    op2_file: Union[str, Path],
    subcase: int = 1,
    # Add other parameters specific to your physics
) -> Dict[str, Any]:
    """
    Extract {physics description} from OP2 file.

    Args:
        op2_file: Path to the OP2 results file
        subcase: Subcase number to extract (default: 1)
        # Document other parameters

    Returns:
        Dictionary containing:
        - '{main_result}': The primary result value ({unit})
        - '{secondary_result}': Secondary result info
        - 'subcase': The subcase extracted
        - 'unit': Unit of the result

    Raises:
        FileNotFoundError: If OP2 file doesn't exist
        KeyError: If subcase not found in results
        ValueError: If result data is invalid

    Example:
        >>> result = extract_{physics}('model.op2', subcase=1)
        >>> print(result['{main_result}'])
        123.45
        >>> print(result['unit'])
        '{unit}'
    """
    # Convert to Path for consistency
    op2_file = Path(op2_file)

    # Validate file exists
    if not op2_file.exists():
        raise FileNotFoundError(f"OP2 file not found: {op2_file}")

    # Read OP2 file
    op2 = OP2()
    op2.read_op2(str(op2_file))

    # =========================================
    # CUSTOMIZE: Your extraction logic here
    # =========================================

    # Example: Access displacement data
    # if subcase not in op2.displacements:
    #     raise KeyError(f"Subcase {subcase} not found in displacement results")
    # data = op2.displacements[subcase]

    # Example: Access stress data
    # if subcase not in op2.cquad4_stress:
    #     raise KeyError(f"Subcase {subcase} not found in stress results")
    # stress_data = op2.cquad4_stress[subcase]

    # Example: Process data
    # values = data.data  # numpy array
    # max_value = values.max()
    # max_index = values.argmax()

    # =========================================
    # Replace with your actual computation
    # =========================================
    main_result = 0.0  # TODO: Compute actual value
    secondary_result = 0  # TODO: Compute actual value

    return {
        '{main_result}': main_result,
        '{secondary_result}': secondary_result,
        'subcase': subcase,
        'unit': '{unit}',
    }


# Optional: Class-based extractor for complex cases
class {Physics}Extractor:
    """
    Class-based extractor for {physics} with state management.

    Use this pattern when:
    - Extraction requires multiple steps
    - You need to cache the OP2 data
    - Configuration is complex

    Example:
        >>> extractor = {Physics}Extractor('model.op2', config={'option': value})
        >>> result = extractor.extract(subcase=1)
        >>> print(result)
    """

    def __init__(
        self,
        op2_file: Union[str, Path],
        bdf_file: Optional[Union[str, Path]] = None,
        **config
    ):
        """
        Initialize the extractor.

        Args:
            op2_file: Path to OP2 results file
            bdf_file: Optional path to BDF mesh file (for node coordinates)
            **config: Additional configuration options
        """
        self.op2_file = Path(op2_file)
        self.bdf_file = Path(bdf_file) if bdf_file else None
        self.config = config
        self._op2 = None  # Lazy-loaded

    def _load_op2(self) -> OP2:
        """Lazy load OP2 file (caches result)."""
        if self._op2 is None:
            self._op2 = OP2()
            self._op2.read_op2(str(self.op2_file))
        return self._op2

    def extract(self, subcase: int = 1) -> Dict[str, Any]:
        """
        Extract results for given subcase.

        Args:
            subcase: Subcase number

        Returns:
            Dictionary with extraction results
        """
        op2 = self._load_op2()

        # TODO: Implement your extraction logic
        # Use self.config for configuration options

        return {
            '{main_result}': 0.0,
            'subcase': subcase,
        }

    def extract_all_subcases(self) -> Dict[int, Dict[str, Any]]:
        """
        Extract results for all available subcases.

        Returns:
            Dictionary mapping subcase number to results
        """
        op2 = self._load_op2()

        # TODO: Find available subcases
        # available_subcases = list(op2.displacements.keys())

        results = {}
        # for sc in available_subcases:
        #     results[sc] = self.extract(subcase=sc)

        return results


# =========================================
# After creating your extractor:
# 1. Add to optimization_engine/extractors/__init__.py:
#    from .extract_{physics} import extract_{physics}
#    __all__ = [..., 'extract_{physics}']
#
# 2. Update docs/protocols/system/SYS_12_EXTRACTOR_LIBRARY.md
#    - Add to Quick Reference table
#    - Add detailed section with example
#
# 3. Create test file: tests/test_extract_{physics}.py
# =========================================
