"""
Base chunker interface using DocumentProcessor pattern
"""

import logging
from abc import ABC, abstractmethod
from typing import List, Optional, Type
from datetime import datetime
from dataclasses import dataclass

from ..models import Document
from ..processing import DocumentProcessor, DocumentProcessorConfig

logger = logging.getLogger(__name__)


@dataclass
class ChunkingConfig(DocumentProcessorConfig):
    """Configuration for document chunking
    文書チャンキングの設定"""
    
    chunk_size: int = 512
    overlap: int = 50
    split_by_sentence: bool = True
    preserve_formatting: bool = False
    min_chunk_size: int = 10
    max_chunk_size: int = 1024


class Chunker(DocumentProcessor):
    """Base interface for document chunking that inherits from DocumentProcessor
    DocumentProcessorを継承する文書チャンキングの基底インターフェース"""
    
    def __init__(self, config: Optional[ChunkingConfig] = None):
        """Initialize chunker with optional configuration
        オプション設定でチャンカーを初期化
        
        Args:
            config: Optional chunking configuration
        """
        super().__init__(config)
        
        logger.info(f"Initialized {self.__class__.__name__} with chunk_size={self.config.chunk_size}")
    
    @classmethod
    def get_config_class(cls) -> Type[ChunkingConfig]:
        """Get the configuration class for this processor
        このプロセッサーの設定クラスを取得
        
        Returns:
            ChunkingConfig class type
        """
        return ChunkingConfig
    
    def process(self, document: Document, config: Optional[ChunkingConfig] = None) -> List[Document]:
        """Process document into chunks (implements DocumentProcessor interface)
        文書をチャンクに処理（DocumentProcessorインターフェースの実装）
        
        Args:
            document: Input document to chunk
            config: Optional chunking configuration override
            
        Returns:
            List of chunk documents with proper metadata
        """
        # Use provided config or fall back to instance config
        chunking_config = config or self.config
        
        logger.debug(f"Chunking document {document.id} with {self.__class__.__name__}")
        
        # Call the abstract chunk method
        chunk_docs = self.chunk(document, chunking_config)
        
        # Add standard chunking metadata to all chunks
        for i, chunk_doc in enumerate(chunk_docs):
            # Ensure proper lineage tracking
            original_id = document.metadata.get("original_document_id", document.id)
            
            chunk_doc.metadata.update({
                "original_document_id": original_id,
                "parent_document_id": document.id,
                "processing_stage": "chunked",
                "chunk_position": i,
                "chunk_total": len(chunk_docs),
                "chunking_method": self.__class__.__name__,
                "chunk_config": chunking_config.to_dict(),
                "chunked_at": datetime.now().isoformat()
            })
            
            # Add token count if available
            token_count = self.estimate_tokens(chunk_doc.content)
            if token_count:
                chunk_doc.metadata["token_count"] = token_count
            
            # Add character positions if available
            if hasattr(self, '_calculate_char_positions'):
                start_char, end_char = self._calculate_char_positions(document.content, chunk_doc.content, i)
                chunk_doc.metadata.update({
                    "start_char": start_char,
                    "end_char": end_char
                })
            
            # Add overlap information if applicable
            if i > 0 and chunking_config.overlap > 0:
                chunk_doc.metadata["overlap_previous"] = chunking_config.overlap
        
        logger.debug(f"Successfully chunked document {document.id} into {len(chunk_docs)} chunks")
        
        return chunk_docs
    
    @abstractmethod
    def chunk(self, document: Document, config: ChunkingConfig) -> List[Document]:
        """Split document into chunk documents (abstract method for subclasses)
        文書をチャンク文書に分割（サブクラス用の抽象メソッド）
        
        Args:
            document: Input document to chunk
            config: Chunking configuration
            
        Returns:
            List of Document objects representing chunks
        """
        pass
    
    @abstractmethod
    def estimate_tokens(self, text: str) -> int:
        """Estimate token count for text (abstract method for subclasses)
        テキストのトークン数を推定（サブクラス用の抽象メソッド）
        
        Args:
            text: Text to estimate tokens for
            
        Returns:
            Estimated number of tokens
        """
        pass
    
    def _generate_chunk_id(self, parent_document_id: str, position: int) -> str:
        """Generate unique chunk ID
        一意のチャンクIDを生成
        
        Args:
            parent_document_id: ID of the parent document
            position: Position of chunk in sequence
            
        Returns:
            Unique chunk ID
        """
        return f"{parent_document_id}_chunk_{position}"
    
    def _validate_chunk_size(self, chunk_text: str, config: ChunkingConfig) -> bool:
        """Validate chunk size against configuration
        設定に対してチャンクサイズを検証
        
        Args:
            chunk_text: Text content of the chunk
            config: Chunking configuration
            
        Returns:
            True if chunk size is valid
        """
        token_count = self.estimate_tokens(chunk_text)
        
        if token_count < config.min_chunk_size:
            logger.warning(f"Chunk size {token_count} is below minimum {config.min_chunk_size}")
            return False
        
        if token_count > config.max_chunk_size:
            logger.warning(f"Chunk size {token_count} exceeds maximum {config.max_chunk_size}")
            return False
        
        return True
    
    def get_chunking_stats(self) -> dict:
        """Get chunking-specific statistics
        チャンキング固有の統計を取得
        
        Returns:
            Dictionary with chunking statistics
        """
        base_stats = self.get_processing_stats()
        
        # Add chunking-specific stats
        chunking_stats = {
            **base_stats,
            "chunker_class": self.__class__.__name__,
            "config": self.config.to_dict(),
            "chunk_size_target": self.config.chunk_size,
            "overlap_size": self.config.overlap
        }
        
        return chunking_stats