from typing import Optional, TYPE_CHECKING, TypedDict
from dataclasses import dataclass

from .base_log_types import BaseLog, BaseLogParams, LogTypeStr, Input, Output

if TYPE_CHECKING:
    from .generation_types import Generation, GenerationParams

class _LogParamsRequired(TypedDict):
    """Required fields for BaseLogParams."""
    type: LogTypeStr

class LogParams(BaseLogParams, _LogParamsRequired, total=False):
    """Parameters for creating or updating a log.

    This class defines the parameters needed to create or update a log entry,
    including its type, input, and output data.

    Attributes:
        type: The type of log entry (e.g., 'span', 'generation') (required).
              Used to distinguish between different kinds of logs.
        input: Optional input data for this operation.
        output: Optional output data generated by the operation.
    """
    ...

class UpdateLogParams(LogParams, total=False):
    """Parameters for updating a log."""
    name: Optional[str]
    type: Optional[LogTypeStr]


@dataclass
class Log(BaseLog):
    """Log interface representing a specific operation or step within a trace.

    Logs are used to track discrete operations within a process flow, such as
    data fetching, validation, or any other logical step. Logs can contain
    generations and can be nested within other logs to create a hierarchical
    structure of operations.

    Example:
        ```python
        # Create a log within a trace
        log = trace.create_log({
            'name': 'data-processing'
        })

        # Start the log with input
        log.start('Raw user data')

        # Create a nested log for a sub-operation
        validation_log = log.create_log({
            'name': 'data-validation'
        })

        # Create a generation within the validation log
        generation = validation_log.create_generation({
            'name': 'validation-check',
            'prompt': {'slug': 'data-validator', 'version': '1.0.0'},
            'input': 'Raw user data'
        })

        # End the generation with output
        generation.end('Data is valid')

        # End the validation log
        validation_log.end('Validation complete')

        # End the main log with processed output
        log.end('Processed user data')
        ```
    """
    def start(self, input: Optional[Input] = None) -> 'Log':
        """Marks the log as started and sets the input if provided.

        Args:
            input: Optional input data to associate with the log.

        Returns:
            The log instance for method chaining.

        Example:
            ```python
            # Start a log without input
            log.start()

            # Start a log with input
            log.start('Raw user data to be processed')
            ```
        """
        ...

    def end(self, output: Optional[Output] = None) -> 'Log':
        """Marks the log as ended and sets the output if provided.

        Args:
            output: Optional output data to associate with the log.

        Returns:
            The log instance for method chaining.

        Example:
            ```python
            # End a log without output
            log.end()

            # End a log with output
            log.end('Processed data: {"success": true, "items": 42}')
            ```
        """
        ...

    def append(self, generation: 'Generation') -> 'Log':
        """Adds a generation to this log.

        Args:
            generation: The generation to add to this log.

        Returns:
            The log instance for method chaining.

        Example:
            ```python
            # Create a generation separately
            generation = monitor_sdk.create_generation({
                'name': 'external-generation',
                'trace': trace
            })

            # Append the generation to this log
            log.append(generation)
            ```
        """
        ...

    def create_generation(self, params: 'GenerationParams') -> 'Generation':
        """Creates a new generation within this log.

        Args:
            params: Parameters for the generation.

        Returns:
            A new Generation instance associated with this log.

        Example:
            ```python
            # Create a generation with a prompt reference
            generation = log.create_generation({
                'name': 'text-analysis',
                'prompt': {'slug': 'text-analyzer', 'version': '1.2.0'},
                'input': 'Analyze this text for sentiment and key topics',
                'variables': {'language': 'en', 'mode': 'detailed'},
                'metadata': {'priority': 'high'}
            })

            # Create a simple generation without a prompt reference
            simple_generation = log.create_generation({
                'name': 'quick-check',
                'input': 'Is this text appropriate?',
                'output': 'Yes, the text is appropriate for all audiences.'
            })
            ```
        """
        ...

    def create_log(self, params: LogParams) -> 'Log':
        """Creates a new nested log within this log.

        Args:
            params: Parameters for the nested log.

        Returns:
            A new Log instance associated with this log as its parent.

        Example:
            ```python
            # Create a basic nested log
            nested_log = log.create_log({
                'name': 'sub-operation'
            })

            # Create a detailed nested log
            detailed_nested_log = log.create_log({
                'name': 'data-transformation',
                'input': 'Raw data format',
                'metadata': {'transformType': 'json-to-xml', 'preserveOrder': True}
            })
            ```
        """
        ...

    def update(self, params: 'UpdateLogParams') -> 'Log':
        """Updates the log with new parameters.

        Args:
            **params: The parameters to update.

        Returns:
            The log instance for method chaining.
        """
        ...
