from typing import Optional, List
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session

from llm_redact.db.models import (
    Base, HistoryRecord, RuleRecord, RedactionResult, Replacement,
    PlaceholderMapping
)
from llm_redact.config import get_settings


class DatabaseManager:
    """Manages database connections and operations"""
    
    def __init__(self, database_url: Optional[str] = None):
        settings = get_settings()
        self.database_url = database_url or settings.database_url
        self.engine = create_engine(self.database_url)
        self.SessionLocal = sessionmaker(bind=self.engine)
        
        # Create tables if they don't exist
        Base.metadata.create_all(bind=self.engine)
    
    def get_session(self) -> Session:
        """Get a database session"""
        return self.SessionLocal()
    
    def _generate_prompt_hash(self, prompt: str) -> str:
        """Generate a hash for the prompt to use as cache key"""
        import hashlib
        return hashlib.sha256(prompt.encode()).hexdigest()
    
    def save_redaction_result(
        self, 
        result: RedactionResult,
        rule_group_id: str = "group_default",
        status: str = "completed"
    ) -> HistoryRecord:
        """Save redaction result to database"""
        import hashlib
        
        # Generate hash ID for history record with prefix
        # Use original text + rule group ID for consistent caching
        history_content = f"{result.original_text}|{rule_group_id}"
        history_hash = hashlib.sha256(history_content.encode()).hexdigest()
        history_id = f"htry_{history_hash[:8]}"
        
        with self.get_session() as session:
            # Convert replacements to JSON format
            replacements_json = [
                {
                    "original_text": r.original_text,
                    "replacement_text": r.replacement_text
                }
                for r in result.replacements
            ]
            
            record = HistoryRecord(
                id=history_id,
                prompt=result.original_text,
                is_redacted=result.is_redacted,
                processed_prompt=result.redacted_text,
                replacements=replacements_json,
                processing_time_ms=result.processing_time_ms,
                rule_group_id=rule_group_id,  # Use rule_id for rule_group_id
                status=status
            )
            
            session.add(record)
            session.commit()
            session.refresh(record)
            return record
    
    def get_cached_result(
        self, 
        prompt: str, 
        rule_group_id: str
    ) -> Optional[RedactionResult]:
        """Get cached redaction result if available"""
        settings = get_settings()
        if not settings.enable_caching:
            return None
            
        # Generate the same hash ID that would be used for this combination
        import hashlib
        history_content = f"{prompt}|{rule_group_id}"
        history_hash = hashlib.sha256(history_content.encode()).hexdigest()
        history_id = f"htry_{history_hash[:8]}"
            
        with self.get_session() as session:
            # Look for exact match by ID (text + rule group combination)
            record = session.query(HistoryRecord).filter(
                HistoryRecord.id == history_id,
                HistoryRecord.status == "completed"
            ).first()
            
            if record:
                # Convert JSON replacements back to Replacement objects
                replacements = [
                    Replacement(
                        original_text=r["original_text"],
                        replacement_text=r["replacement_text"]
                    )
                    for r in record.replacements
                ]
                
                return RedactionResult(
                    original_text=record.prompt,
                    redacted_text=record.processed_prompt,
                    replacements=replacements,
                    is_redacted=record.is_redacted,
                    processing_time_ms=record.processing_time_ms,
                    rule_group_id=record.rule_group_id,
                    cached=True
                )
        
        return None
    
    def get_history(self, limit: int = 100) -> List[HistoryRecord]:
        """Get redaction history"""
        with self.get_session() as session:
            return session.query(HistoryRecord).order_by(
                HistoryRecord.created_at.desc()
            ).limit(limit).all()
    
    def create_rule(
        self, name: str, description: str, data_type: str
    ) -> RuleRecord:
        """Create a new redaction rule"""
        with self.get_session() as session:
            rule = RuleRecord(
                name=name, description=description, data_type=data_type
            )
            session.add(rule)
            session.commit()
            session.refresh(rule)
            return rule
    
    def get_active_rules(self) -> List[RuleRecord]:
        """Get all active redaction rules"""
        with self.get_session() as session:
            return session.query(RuleRecord).filter(
                RuleRecord.is_active,
                ~RuleRecord.is_archived
            ).all()


# Global database manager instance
_db_manager = None


def get_db_manager() -> DatabaseManager:
    """Get the global database manager instance"""
    global _db_manager
    if _db_manager is None:
        _db_manager = DatabaseManager()
    return _db_manager


def configure_database(database_url: str):
    """Configure database with custom URL"""
    global _db_manager
    _db_manager = DatabaseManager(database_url)


def create_placeholder_with_id(original_text: str, data_type: str) -> str:
    """Create a placeholder with unique ID and store mapping"""
    import hashlib
    
    # Generate ID: data_type + hash
    full_hash = hashlib.sha256(original_text.encode()).hexdigest()
    short_hash = full_hash[:8].upper()
    placeholder_id = f"{data_type}_{short_hash}"
    
    # Create placeholder text
    placeholder = f"|_{placeholder_id}_|"
    
    # Store mapping
    db_manager = get_db_manager()
    with db_manager.get_session() as session:
        # Check if mapping already exists
        existing = session.query(PlaceholderMapping).filter_by(
            id=placeholder_id
        ).first()
        
        if existing:
            # Update usage count
            existing.usage_count += 1
            session.commit()
        else:
            # Create new mapping
            mapping = PlaceholderMapping(
                id=placeholder_id,
                original_text=original_text
            )
            session.add(mapping)
            session.commit()
    
    return placeholder


def get_original_text_from_placeholder(placeholder: str) -> Optional[str]:
    """Get original text from placeholder by looking up mapping"""
    import re
    
    # Extract placeholder ID from format |_ID_|
    match = re.match(r'\|_([A-Z_]+_[A-F0-9]{8})_\|', placeholder)
    if not match:
        return None
    
    placeholder_id = match.group(1)
    
    db_manager = get_db_manager()
    with db_manager.get_session() as session:
        mapping = session.query(PlaceholderMapping).filter_by(
            id=placeholder_id
        ).first()
        
        if mapping:
            return mapping.original_text
    
    return None 