"""
SQLite-based storage backend.

This module implements storage backends using SQLite for efficient
key-value storage and full-text search capabilities.
"""

import logging
import sqlite3
import json
import os
import fnmatch
from typing import Any, Dict, Optional, List, Tuple, Iterator
from .storage_interface import StorageInterface, FileMetadataInterface, SearchInterface, DALInterface

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class SQLiteStorage(StorageInterface):
    """SQLite-based key-value storage with FTS support."""
    
    def __init__(self, db_path: str):
        """Initialize SQLite storage.
        
        Args:
            db_path: Path to SQLite database file
        """
        self.db_path = db_path
        logger.debug(f"Initializing SQLiteStorage with db_path: {self.db_path}")
        self._ensure_db_directory()
        self._init_db()
    
    def _ensure_db_directory(self):
        """Ensure the directory for the database exists."""
        db_dir = os.path.dirname(self.db_path)
        if db_dir and not os.path.exists(db_dir):
            os.makedirs(db_dir, exist_ok=True)
    
    def _init_db(self):
        """Initialize the database schema with critical PRAGMA settings."""
        with sqlite3.connect(self.db_path) as conn:
            # CRITICAL: Set PRAGMA settings for durability and consistency
            # PRAGMA synchronous = FULL: Ensure all writes are synced to disk
            # This provides maximum durability at the cost of some performance
            conn.execute('PRAGMA synchronous = FULL')

            # PRAGMA journal_mode = WAL: Use Write-Ahead Logging for better concurrency
            # WAL mode allows readers to proceed without blocking writers
            conn.execute('PRAGMA journal_mode = WAL')

            # PRAGMA foreign_keys = ON: Enforce foreign key constraints
            # Although not used in kv_store, this is a good default for all SQLite DBs
            conn.execute('PRAGMA foreign_keys = ON')

            # Create main key-value table
            conn.execute('''
                CREATE TABLE IF NOT EXISTS kv_store (
                    key TEXT PRIMARY KEY,
                    value BLOB,
                    value_type TEXT,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
            ''')
            conn.commit()
    
    def put(self, key: str, value: Any) -> bool:
        """Store a key-value pair."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                if isinstance(value, str):
                    value_blob = value.encode('utf-8')
                    value_type = 'text'
                else:
                    value_blob = json.dumps(value).encode('utf-8')
                    value_type = 'json'
                
                conn.execute('''
                    INSERT OR REPLACE INTO kv_store (key, value, value_type, updated_at)
                    VALUES (?, ?, ?, CURRENT_TIMESTAMP)
                ''', (key, value_blob, value_type))
                
                conn.commit()
                return True
        except Exception as e:
            logger.error(f"Error storing key {key}: {e}")
            return False
    
    def get(self, key: str) -> Optional[Any]:
        """Retrieve a value by key."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute(
                    'SELECT value, value_type FROM kv_store WHERE key = ?',
                    (key,)
                )
                row = cursor.fetchone()
                
                if row is None:
                    return None
                
                value_blob, value_type = row
                if value_type == 'text':
                    return value_blob.decode('utf-8')
                else:
                    return json.loads(value_blob.decode('utf-8'))
                    
        except Exception as e:
            logger.error(f"Error retrieving key {key}: {e}")
            return None
    
    def delete(self, key: str) -> bool:
        """Delete a key-value pair."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('DELETE FROM kv_store WHERE key = ?', (key,))
                conn.commit()
                return cursor.rowcount > 0
        except Exception as e:
            logger.error(f"Error deleting key {key}: {e}")
            return False
    
    def exists(self, key: str) -> bool:
        """Check if a key exists."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute(
                    'SELECT 1 FROM kv_store WHERE key = ? LIMIT 1',
                    (key,)
                )
                return cursor.fetchone() is not None
        except Exception as e:
            logger.error(f"Error checking key existence {key}: {e}")
            return False
    
    def keys(self, pattern: Optional[str] = None) -> Iterator[str]:
        """Iterate over keys, optionally filtered by pattern."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                if pattern:
                    cursor = conn.execute('SELECT key FROM kv_store ORDER BY key')
                    for row in cursor:
                        key = row[0]
                        if fnmatch.fnmatch(key, pattern):
                            yield key
                else:
                    cursor = conn.execute('SELECT key FROM kv_store ORDER BY key')
                    for row in cursor:
                        yield row[0]
        except Exception as e:
            logger.error(f"Error iterating keys: {e}")
    
    def items(self, pattern: Optional[str] = None) -> Iterator[Tuple[str, Any]]:
        """Iterate over key-value pairs, optionally filtered by pattern."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('SELECT key, value, value_type FROM kv_store ORDER BY key')
                for row in cursor:
                    key, value_blob, value_type = row
                    if pattern and not fnmatch.fnmatch(key, pattern):
                        continue
                    
                    if value_type == 'text':
                        value = value_blob.decode('utf-8')
                    else:
                        value = json.loads(value_blob.decode('utf-8'))
                    
                    yield key, value
        except Exception as e:
            logger.error(f"Error iterating items: {e}")
    
    def clear(self) -> bool:
        """Clear all data."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute('DELETE FROM kv_store')
                conn.commit()
                # Ensure tables are properly initialized after clearing
                self._init_db()
                return True
        except Exception as e:
            logger.error(f"Error clearing data: {e}")
            # Try to reinitialize the database in case of schema issues
            try:
                self._init_db()
                return True
            except Exception as init_e:
                logger.error(f"Error reinitializing database after clear: {init_e}")
                return False
    
    def size(self) -> int:
        """Get the number of stored items."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('SELECT COUNT(*) FROM kv_store')
                return cursor.fetchone()[0]
        except Exception as e:
            logger.error(f"Error getting size: {e}")
            return 0
    
    def close(self) -> None:
        """Close the storage backend."""
        # SQLite connections are managed per-operation, so no persistent connection to close
        pass

    def flush(self) -> None:
        """
        Flushes any pending writes to the storage.
        For SQLite, this is generally a no-op as transactions are committed per operation.
        """
        pass

class SQLiteFileMetadata(FileMetadataInterface):
    """SQLite-based file metadata storage, including versions and diffs."""

    def __init__(self, db_path: str):
        """Initialize SQLite file metadata storage.
        
        Args:
            db_path: Path to SQLite database file
        """
        self.db_path = db_path
        self._ensure_db_directory()
        self._init_db()
    
    def _ensure_db_directory(self):
        """Ensure the directory for the database exists."""
        db_dir = os.path.dirname(self.db_path)
        if db_dir and not os.path.exists(db_dir):
            os.makedirs(db_dir, exist_ok=True)
    
    def _init_db(self):
        """Initialize the database schema with critical PRAGMA settings."""
        with sqlite3.connect(self.db_path) as conn:
            # CRITICAL: Set PRAGMA settings for durability and consistency
            # PRAGMA synchronous = FULL: Ensure all writes are synced to disk
            conn.execute('PRAGMA synchronous = FULL')

            # PRAGMA journal_mode = WAL: Use Write-Ahead Logging for better concurrency
            conn.execute('PRAGMA journal_mode = WAL')

            # PRAGMA foreign_keys = ON: Enforce foreign key constraints
            # This is critical for file_diffs table which has foreign keys
            conn.execute('PRAGMA foreign_keys = ON')

            # Create files table
            conn.execute('''
                CREATE TABLE IF NOT EXISTS files (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    file_path TEXT UNIQUE NOT NULL,
                    file_type TEXT NOT NULL,
                    extension TEXT,
                    metadata TEXT,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
            ''')

            # Create indexes for efficient lookups
            conn.execute('''
                CREATE INDEX IF NOT EXISTS idx_files_path ON files(file_path)
            ''')

            conn.execute('''
                CREATE INDEX IF NOT EXISTS idx_files_extension ON files(extension)
            ''')

            conn.execute('''
                CREATE INDEX IF NOT EXISTS idx_files_type ON files(file_type)
            ''')

            # Create file_versions table
            conn.execute('''
                CREATE TABLE IF NOT EXISTS file_versions (
                    version_id TEXT PRIMARY KEY,
                    file_path TEXT NOT NULL,
                    content BLOB NOT NULL,
                    hash TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    size INTEGER NOT NULL
                )
            ''')

            # Create file_diffs table
            conn.execute('''
                CREATE TABLE IF NOT EXISTS file_diffs (
                    diff_id TEXT PRIMARY KEY,
                    file_path TEXT NOT NULL,
                    previous_version_id TEXT,
                    current_version_id TEXT NOT NULL,
                    diff_content BLOB NOT NULL,
                    diff_type TEXT NOT NULL,
                    operation_type TEXT NOT NULL,
                    operation_details TEXT,
                    timestamp TEXT NOT NULL,
                    FOREIGN KEY (previous_version_id) REFERENCES file_versions(version_id),
                    FOREIGN KEY (current_version_id) REFERENCES file_versions(version_id)
                )
            ''')
            conn.commit()
    
    def add_file(self, file_path: str, file_type: str, extension: str,
                 metadata: Optional[Dict[str, Any]] = None) -> bool:
        """Add a file to the index."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                metadata_json = json.dumps(metadata) if metadata else None
                conn.execute('''
                    INSERT OR REPLACE INTO files (file_path, file_type, extension, metadata, updated_at)
                    VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
                ''', (file_path, file_type, extension, metadata_json))
                conn.commit()
                return True
        except Exception as e:
            logger.error(f"Error adding file {file_path}: {e}")
            return False
    
    def remove_file(self, file_path: str) -> bool:
        """Remove a file from the index."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('DELETE FROM files WHERE file_path = ?', (file_path,))
                conn.commit()
                return cursor.rowcount > 0
        except Exception as e:
            logger.error(f"Error removing file {file_path}: {e}")
            return False
    
    def get_file_info(self, file_path: str) -> Optional[Dict[str, Any]]:
        """Get information about a file."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('''
                    SELECT file_type, extension, metadata
                    FROM files WHERE file_path = ?
                ''', (file_path,))
                row = cursor.fetchone()
                
                if row is None:
                    return None
                
                file_type, extension, metadata_json = row
                metadata = json.loads(metadata_json) if metadata_json else {}
                
                return {
                    'type': file_type,
                    'extension': extension,
                    'path': file_path,
                    **metadata
                }
        except Exception as e:
            logger.error(f"Error getting file info for {file_path}: {e}")
            return None
    
    def get_directory_structure(self, directory_path: str = "") -> Dict[str, Any]:
        """Get the directory structure."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                if directory_path:
                    cursor = conn.execute('''
                        SELECT file_path, file_type, extension, metadata
                        FROM files
                        WHERE file_path LIKE ?
                        ORDER BY file_path
                    ''', (f"{directory_path}%",))
                else:
                    cursor = conn.execute('''
                        SELECT file_path, file_type, extension, metadata
                        FROM files
                        ORDER BY file_path
                    ''')
                
                structure = {}
                for row in cursor.fetchall():
                    file_path, file_type, extension, metadata_json = row
                    metadata = json.loads(metadata_json) if metadata_json else {}
                    
                    # Build nested structure
                    current = structure
                    parts = file_path.split('/')
                    
                    for i, part in enumerate(parts):
                        if i == len(parts) - 1:  # Last part (file or directory)
                            if file_type == 'file':
                                current[part] = {
                                    'type': file_type,
                                    'path': file_path,
                                    'ext': extension,
                                    **metadata
                                }
                            else:
                                if part not in current:
                                    current[part] = {'type': 'directory', 'children': {}}
                                current = current[part]['children']
                        else:  # Directory part
                            if part not in current:
                                current[part] = {'type': 'directory', 'children': {}}
                            current = current[part]['children']
                
                return structure
        except Exception as e:
            logger.error(f"Error getting directory structure: {e}")
            return {}
    
    def get_all_files(self) -> List[Tuple[str, Dict[str, Any]]]:
        """Get all files in the index."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('''
                    SELECT file_path, file_type, extension, metadata
                    FROM files
                    ORDER BY file_path
                ''')
                
                files = []
                for row in cursor.fetchall():
                    file_path, file_type, extension, metadata_json = row
                    metadata = json.loads(metadata_json) if metadata_json else {}
                    
                    file_info = {
                        'type': file_type,
                        'extension': extension,
                        'path': file_path,
                        **metadata
                    }
                    files.append((file_path, file_info))
                
                return files
        except Exception as e:
            logger.error(f"Error getting all files: {e}")
            return []
    
    def insert_file_version(self, version_id: str, file_path: str, content: str, hash: str, timestamp: str, size: int) -> bool:
        """Inserts a new file version into the file_versions table."""
        try:
            sql = '''
                INSERT INTO file_versions (version_id, file_path, content, hash, timestamp, size)
                VALUES (?, ?, ?, ?, ?, ?)
            '''
            params = (version_id, file_path, content.encode('utf-8'), hash, timestamp, size)
            logger.debug(f"Attempting to insert file version. SQL: {sql} Params: {params}")
            with sqlite3.connect(self.db_path) as conn:
                conn.execute(sql, params)
                conn.commit()
                logger.info(f"Successfully inserted file version: {version_id}")
                return True
        except Exception as e:
            logger.error(f"Error inserting file version {version_id} for {file_path}: {e}")
            return False

    def insert_file_diff(self, diff_id: str, file_path: str, previous_version_id: Optional[str], current_version_id: str, diff_content: str, diff_type: str, operation_type: str, operation_details: Optional[str], timestamp: str) -> bool:
        """Inserts a new file diff into the file_diffs table."""
        try:
            sql = '''
                INSERT INTO file_diffs (diff_id, file_path, previous_version_id, current_version_id, diff_content, diff_type, operation_type, operation_details, timestamp)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            '''
            params = (diff_id, file_path, previous_version_id, current_version_id, diff_content.encode('utf-8'), diff_type, operation_type, operation_details, timestamp)
            logger.debug(f"Attempting to insert file diff. SQL: {sql} Params: {params}")
            with sqlite3.connect(self.db_path) as conn:
                conn.execute(sql, params)
                conn.commit()
                logger.info(f"Successfully inserted file diff: {diff_id}")
                return True
        except Exception as e:
            logger.error(f"Error inserting file diff {diff_id} for {file_path}: {e}")
            return False

    def get_file_version(self, version_id: str) -> Optional[Dict]:
        """Retrieves a file version by its ID."""
        try:
            sql = 'SELECT * FROM file_versions WHERE version_id = ?'
            params = (version_id,)
            logger.debug(f"Attempting to retrieve file version. SQL: {sql} Params: {params}")
            with sqlite3.connect(self.db_path) as conn:
                conn.row_factory = sqlite3.Row
                cursor = conn.execute(sql, params)
                row = cursor.fetchone()
                if row:
                    version_data = dict(row)
                    version_data['content'] = version_data['content'].decode('utf-8')
                    logger.debug(f"Retrieved file version: {version_id}. Data: {version_data}")
                    return version_data
                logger.debug(f"File version {version_id} not found.")
                return None
        except Exception as e:
            logger.error(f"Error retrieving file version {version_id}: {e}")
            return None

    def get_file_diffs_for_path(self, file_path: str) -> List[Dict]:
        """Retrieves all diffs for a given file path."""
        try:
            # Normalize path for consistent querying
            normalized_file_path = os.path.normpath(file_path).replace('\\', '/')

            # Try multiple path variations to handle normalization inconsistencies
            search_paths = [normalized_file_path]

            # Add alternative path formats
            if normalized_file_path.startswith('/'):
                search_paths.append(normalized_file_path[1:])  # Remove leading slash
            else:
                search_paths.append('/' + normalized_file_path)  # Add leading slash

            # Try exact match first, then LIKE patterns
            sql_exact = 'SELECT * FROM file_diffs WHERE file_path = ? ORDER BY timestamp ASC'
            sql_like = 'SELECT * FROM file_diffs WHERE file_path LIKE ? ORDER BY timestamp ASC'

            diffs = []

            with sqlite3.connect(self.db_path) as conn:
                conn.row_factory = sqlite3.Row

                # Try exact matches first
                for search_path in search_paths:
                    cursor = conn.execute(sql_exact, (search_path,))
                    path_diffs = []
                    for row in cursor.fetchall():
                        diff_data = dict(row)
                        diff_data['diff_content'] = diff_data['diff_content'].decode('utf-8')
                        path_diffs.append(diff_data)

                    if path_diffs:
                        diffs.extend(path_diffs)
                        logger.debug(f"Found {len(path_diffs)} diffs with exact match for {search_path}")
                        break

                # If no exact matches, try LIKE patterns for partial matches
                if not diffs:
                    for search_path in search_paths:
                        cursor = conn.execute(sql_like, (f"%{search_path}%",))
                        path_diffs = []
                        for row in cursor.fetchall():
                            diff_data = dict(row)
                            diff_data['diff_content'] = diff_data['diff_content'].decode('utf-8')
                            path_diffs.append(diff_data)

                        if path_diffs:
                            diffs.extend(path_diffs)
                            logger.debug(f"Found {len(path_diffs)} diffs with LIKE match for {search_path}")
                            break

                logger.debug(f"Retrieved {len(diffs)} diffs for {file_path}")
                return diffs
        except Exception as e:
            logger.error(f"Error retrieving file diffs for {file_path}: {e}")
            return []

    def get_file_versions_for_path(self, file_path: str) -> List[Dict]:
        """Retrieves all versions for a given file path, ordered by timestamp."""
        try:
            # Normalize path for consistent querying
            normalized_file_path = os.path.normpath(file_path).replace('\\', '/')

            # Try multiple path variations to handle normalization inconsistencies
            search_paths = [normalized_file_path]

            # Add alternative path formats
            if normalized_file_path.startswith('/'):
                search_paths.append(normalized_file_path[1:])  # Remove leading slash
            else:
                search_paths.append('/' + normalized_file_path)  # Add leading slash

            # Try exact match first, then LIKE patterns
            sql_exact = 'SELECT * FROM file_versions WHERE file_path = ? ORDER BY timestamp ASC'
            sql_like = 'SELECT * FROM file_versions WHERE file_path LIKE ? ORDER BY timestamp ASC'

            versions = []

            with sqlite3.connect(self.db_path) as conn:
                conn.row_factory = sqlite3.Row

                # Try exact matches first
                for search_path in search_paths:
                    cursor = conn.execute(sql_exact, (search_path,))
                    path_versions = []
                    for row in cursor.fetchall():
                        version_data = dict(row)
                        version_data['content'] = version_data['content'].decode('utf-8')
                        path_versions.append(version_data)

                    if path_versions:
                        versions.extend(path_versions)
                        logger.debug(f"Found {len(path_versions)} versions with exact match for {search_path}")
                        break

                # If no exact matches, try LIKE patterns for partial matches
                if not versions:
                    for search_path in search_paths:
                        cursor = conn.execute(sql_like, (f"%{search_path}%",))
                        path_versions = []
                        for row in cursor.fetchall():
                            version_data = dict(row)
                            version_data['content'] = version_data['content'].decode('utf-8')
                            path_versions.append(version_data)

                        if path_versions:
                            versions.extend(path_versions)
                            logger.debug(f"Found {len(path_versions)} versions with LIKE match for {search_path}")
                            break

                logger.debug(f"Retrieved {len(versions)} versions for {file_path}")
                return versions
        except Exception as e:
            logger.error(f"Error retrieving file versions for {file_path}: {e}")
            return []
    
    def clear(self) -> bool:
        """Clear all files from the index."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute('DELETE FROM files')
                conn.execute('DELETE FROM file_versions')
                conn.execute('DELETE FROM file_diffs')
                conn.commit()
                # Ensure tables are properly initialized after clearing
                self._init_db()
                return True
        except Exception as e:
            logger.error(f"Error clearing file index: {e}")
            # Try to reinitialize the database in case of schema issues
            try:
                self._init_db()
                return True
            except Exception as init_e:
                logger.error(f"Error reinitializing file index after clear: {init_e}")
                return False
    
    def size(self) -> int:
        """Get the number of files in the index."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('SELECT COUNT(*) FROM files')
                return cursor.fetchone()[0]
        except Exception as e:
            logger.error(f"Error getting file index size: {e}")
            return 0
    
    def close(self) -> None:
        """Close the storage backend."""
        # SQLite connections are managed per-operation, so no persistent connection to close
        pass

    def save_file_metadata(self, file_path: str, metadata: Dict[str, Any]) -> None:
        """Save file metadata to storage.

        Args:
            file_path: The path of the file
            metadata: The metadata dictionary to save

        Raises:
            IOError: If the metadata cannot be written
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                metadata_json = json.dumps(metadata)
                conn.execute('''
                    UPDATE files SET metadata = ?, updated_at = CURRENT_TIMESTAMP
                    WHERE file_path = ?
                ''', (metadata_json, file_path))
                conn.commit()
        except Exception as e:
            logger.error(f"Error saving metadata for {file_path}: {e}")
            raise IOError(f"Failed to save metadata for {file_path}: {e}")

    def get_file_metadata(self, file_path: str) -> Optional[Dict[str, Any]]:
        """Retrieve file metadata from storage.

        Args:
            file_path: The path of the file

        Returns:
            The metadata dictionary if found, None otherwise
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('''
                    SELECT metadata FROM files WHERE file_path = ?
                ''', (file_path,))
                row = cursor.fetchone()
                if row and row[0]:
                    return json.loads(row[0])
                return None
        except Exception as e:
            logger.error(f"Error getting metadata for {file_path}: {e}")
            return None

    def delete_file_metadata(self, file_path: str) -> None:
        """Delete file metadata from storage.

        Args:
            file_path: The path of the file
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute('DELETE FROM files WHERE file_path = ?', (file_path,))
                conn.commit()
        except Exception as e:
            logger.error(f"Error deleting metadata for {file_path}: {e}")

    def get_all_file_paths(self) -> List[str]:
        """Get all file paths in the storage.

        Returns:
            List of all file paths
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('SELECT file_path FROM files ORDER BY file_path')
                return [row[0] for row in cursor.fetchall()]
        except Exception as e:
            logger.error(f"Error getting all file paths: {e}")
            return []

    def flush(self) -> bool:
        """Flush any pending operations.

        Returns:
            True if successful, False otherwise
        """
        # SQLite operations are auto-committed, so this is a no-op
        return True


class SQLiteSearch(SearchInterface):
    """SQLite-based search capabilities."""

    def __init__(self, db_path: str, enable_fts: bool = True):
        """Initialize SQLite search.

        Args:
            db_path: Path to SQLite database file
            enable_fts: Whether to enable Full-Text Search (FTS) tables.
        """
        self.db_path = db_path
        self.enable_fts = enable_fts
        self._fts_validated = False  # CRITICAL FIX: Track FTS validation status
        self._ensure_db_directory()
        self._init_db()
        # CRITICAL FIX: Perform FTS integrity check at initialization, not during search
        if self.enable_fts:
            self._ensure_fts_integrity()

    def _ensure_db_directory(self):
        """Ensure the directory for the database exists."""
        db_dir = os.path.dirname(self.db_path)
        if db_dir and not os.path.exists(db_dir):
            os.makedirs(db_dir, exist_ok=True)
    
    def _init_db(self):
        """Initialize the database schema for search with critical PRAGMA settings."""
        with sqlite3.connect(self.db_path) as conn:
            # CRITICAL: Set PRAGMA settings for durability and consistency
            # PRAGMA synchronous = FULL: Ensure all writes are synced to disk
            conn.execute('PRAGMA synchronous = FULL')

            # PRAGMA journal_mode = WAL: Use Write-Ahead Logging for better concurrency
            conn.execute('PRAGMA journal_mode = WAL')

            # PRAGMA foreign_keys = ON: Enforce foreign key constraints
            conn.execute('PRAGMA foreign_keys = ON')

            if self.enable_fts:
                # Create FTS table for file paths
                conn.execute('''
                    CREATE VIRTUAL TABLE IF NOT EXISTS files_fts USING fts5(
                        file_path, content='files', content_rowid='id'
                    )
                ''')

                # Create FTS table for kv_store values (content search)
                conn.execute('''
                    CREATE VIRTUAL TABLE IF NOT EXISTS kv_fts USING fts5(
                        key, value_text, content='kv_store', content_rowid='rowid'
                    )
                ''')

            conn.commit()

    def _validate_fts_tables(self) -> Dict[str, Any]:
        """Validate that FTS tables exist and are properly structured."""
        validation_result = {
            "tables_exist": False,
            "tables_have_data": False,
            "data_consistent": False,
            "needs_repair": False,
            "error": None
        }

        try:
            with sqlite3.connect(self.db_path) as conn:
                # Check if FTS tables exist
                cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table' AND name IN ('kv_fts', 'files_fts')")
                existing_tables = [row[0] for row in cursor.fetchall()]

                if 'kv_fts' not in existing_tables or 'files_fts' not in existing_tables:
                    logger.warning("FTS tables missing, need to recreate them")
                    validation_result["needs_repair"] = True
                    validation_result["error"] = "FTS tables missing"
                    return validation_result

                validation_result["tables_exist"] = True

                # Check if FTS tables have data
                cursor = conn.execute("SELECT COUNT(*) FROM kv_fts")
                kv_count = cursor.fetchone()[0]

                cursor = conn.execute("SELECT COUNT(*) FROM files_fts")
                files_count = cursor.fetchone()[0]

                # Check data consistency
                cursor = conn.execute("SELECT COUNT(*) FROM kv_store")
                kv_store_count = cursor.fetchone()[0]

                cursor = conn.execute("SELECT COUNT(*) FROM files")
                files_table_count = cursor.fetchone()[0]

                validation_result["tables_have_data"] = kv_count > 0 or files_count > 0
                validation_result["data_consistent"] = (kv_count == kv_store_count and files_count == files_table_count)

                if not validation_result["data_consistent"]:
                    logger.warning(f"FTS data inconsistency detected. kv_store: {kv_store_count}, kv_fts: {kv_count}, files: {files_table_count}, files_fts: {files_count}")
                    validation_result["needs_repair"] = True
                    validation_result["error"] = "FTS data inconsistency"

                logger.debug(f"FTS validation: kv_fts has {kv_count}/{kv_store_count} entries, files_fts has {files_count}/{files_table_count} entries")
                return validation_result

        except Exception as e:
            logger.error(f"Error validating FTS tables: {e}")
            validation_result["error"] = str(e)
            validation_result["needs_repair"] = True
            return validation_result

    def _repair_fts_tables(self) -> Dict[str, Any]:
        """Repair FTS tables by rebuilding them from existing data."""
        repair_result = {
            "success": False,
            "kv_fts_repaired": False,
            "files_fts_repaired": False,
            "error": None
        }

        try:
            logger.info("Attempting to repair FTS tables")
            with sqlite3.connect(self.db_path) as conn:
                # First, ensure FTS tables exist
                self._init_db()

                # Repair kv_fts from kv_store data
                try:
                    conn.execute("DELETE FROM kv_fts")
                    conn.execute('''
                        INSERT OR IGNORE INTO kv_fts (rowid, key, value_text)
                        SELECT rowid, key, CASE
                            WHEN value_type = 'text' THEN CAST(value AS TEXT)
                            ELSE json_extract(CAST(value AS TEXT), '$')
                        END
                        FROM kv_store
                        WHERE value IS NOT NULL
                    ''')
                    repair_result["kv_fts_repaired"] = True
                    logger.debug("kv_fts table repaired")
                except Exception as e:
                    logger.error(f"Error repairing kv_fts: {e}")
                    repair_result["error"] = f"kv_fts repair failed: {e}"

                # Repair files_fts from files table data
                try:
                    conn.execute("DELETE FROM files_fts")
                    conn.execute('''
                        INSERT OR IGNORE INTO files_fts (rowid, file_path)
                        SELECT id, file_path
                        FROM files
                        WHERE file_path IS NOT NULL
                    ''')
                    repair_result["files_fts_repaired"] = True
                    logger.debug("files_fts table repaired")
                except Exception as e:
                    logger.error(f"Error repairing files_fts: {e}")
                    if repair_result["error"]:
                        repair_result["error"] += f"; files_fts repair failed: {e}"
                    else:
                        repair_result["error"] = f"files_fts repair failed: {e}"

                conn.commit()

                repair_result["success"] = repair_result["kv_fts_repaired"] or repair_result["files_fts_repaired"]

                if repair_result["success"]:
                    logger.info("FTS tables repair completed")
                else:
                    logger.error("FTS tables repair failed completely")

                return repair_result

        except Exception as e:
            logger.error(f"Error during FTS table repair: {e}")
            repair_result["error"] = str(e)
            return repair_result

    def _ensure_fts_integrity(self) -> bool:
        """
        Ensure FTS tables are healthy and repair if necessary.

        CRITICAL FIX: Now called at initialization instead of during search to prevent
        performance degradation in the search path. Also tracks _fts_validated state.

        NOTE: This method was previously duplicated (lines 789-810 contained an older
        version without _fts_validated state tracking). The duplicate was removed.
        """
        try:
            validation = self._validate_fts_tables()

            if validation["needs_repair"]:
                logger.info("FTS tables need repair, attempting automatic repair")
                repair_result = self._repair_fts_tables()

                if repair_result["success"]:
                    logger.info("FTS tables successfully repaired")
                    self._fts_validated = True
                    return True
                else:
                    logger.error(f"FTS table repair failed: {repair_result['error']}")
                    self._fts_validated = False
                    return False
            else:
                logger.debug("FTS tables are healthy")
                self._fts_validated = True
                return True

        except Exception as e:
            logger.error(f"Error ensuring FTS integrity: {e}")
            self._fts_validated = False
            return False

    def search_content(self, query: str, is_regex: bool = False) -> List[Tuple[str, Any]]:
        """
        Search across file content using FTS with improved error handling.

        CRITICAL FIX: Removed integrity check from search path for performance.
        Integrity checks are now performed during initialization only.
        """
        logger.debug(f"SQLite search_content called with query='{query}', is_regex={is_regex}")

        # CRITICAL FIX: Only validate FTS if not previously validated or on error
        # Don't run integrity checks on every search call
        if not self._fts_validated and self.enable_fts:
            logger.warning("FTS not validated, performing one-time validation")
            if not self._ensure_fts_integrity():
                logger.error("FTS integrity check failed, cannot perform search")
                return []

        # Validate and prepare the search query
        search_query = self._prepare_search_query(query, is_regex)
        if search_query is None:
            logger.error(f"Failed to prepare search query for: {query}")
            return []

        try:
            with sqlite3.connect(self.db_path) as conn:
                if is_regex:
                    # For regex patterns, use REGEXP operator
                    cursor = conn.execute('''
                        SELECT kv_store.key, kv_store.value, kv_store.value_type
                        FROM kv_fts
                        JOIN kv_store ON kv_fts.rowid = kv_store.rowid
                        WHERE kv_fts.value_text REGEXP ?
                        ORDER BY rank
                    ''', (search_query,))
                else:
                    # Standard FTS search with MATCH
                    cursor = conn.execute('''
                        SELECT kv_store.key, kv_store.value, kv_store.value_type
                        FROM kv_fts
                        JOIN kv_store ON kv_fts.rowid = kv_store.rowid
                        WHERE kv_fts MATCH ?
                        ORDER BY rank
                    ''', (search_query,))

                results = []
                row_count = 0
                for row in cursor:
                    row_count += 1
                    key, value_blob, value_type = row

                    try:
                        if value_type == 'text':
                            value = value_blob.decode('utf-8') if isinstance(value_blob, bytes) else str(value_blob)
                        else:
                            # Handle JSON data safely
                            json_str = value_blob.decode('utf-8') if isinstance(value_blob, bytes) else str(value_blob)
                            value = json.loads(json_str)

                        results.append((key, value))
                    except (UnicodeDecodeError, json.JSONDecodeError) as e:
                        logger.warning(f"Error decoding result for key {key}: {e}")
                        continue

                logger.debug(f"SQLite search returned {len(results)} results (processed {row_count} rows)")
                if results:
                    logger.debug(f"First result key: {results[0][0]}")
                return results

        except sqlite3.Error as e:
            logger.error(f"SQLite error during search: {e}")
            # CRITICAL FIX: Mark FTS as invalidated so next search will revalidate
            self._fts_validated = False
            # Try to repair FTS tables if database error occurs
            if self._repair_fts_tables()["success"]:
                logger.info("FTS repair completed, but search needs to be retried")
            return []
        except Exception as e:
            logger.error(f"Unexpected error during search: {e}")
            return []

    def _prepare_search_query(self, query: str, is_regex: bool) -> Optional[str]:
        """Prepare and validate search query for SQLite FTS."""
        if not query or not query.strip():
            return None

        try:
            if is_regex:
                # For regex, validate the pattern
                import re
                re.compile(query, re.MULTILINE | re.IGNORECASE)
                return query
            else:
                # For FTS MATCH, ensure proper escaping
                # SQLite FTS has specific requirements for MATCH queries
                return self._escape_fts_query(query)
        except re.error as e:
            logger.error(f"Invalid regex pattern '{query}': {e}")
            return None
        except Exception as e:
            logger.error(f"Error preparing search query '{query}': {e}")
            return None

    def _escape_fts_query(self, query: str) -> str:
        """Escape special characters for SQLite FTS MATCH queries."""
        if not query:
            return query

        # SQLite FTS special characters that need escaping
        # We need to handle quotes and other special characters
        escaped = query.replace('"', '""')  # Double quotes need to be doubled

        # Handle other special characters that might cause issues
        escaped = escaped.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ')

        return escaped.strip()

    def search_file_paths(self, query: str) -> List[str]:
        """Search across file paths using FTS."""
        try:
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute('''
                    SELECT files.file_path
                    FROM files_fts
                    JOIN files ON files_fts.rowid = files.id
                    WHERE files_fts MATCH ?
                ''', (query,))
                
                return [row[0] for row in cursor.fetchall()]
        except Exception as e:
            logger.error(f"Error searching file paths: {e}")
            return []

    def index_document(self, doc_id: str, document: Dict[str, Any]) -> bool:
        """Index a document for search in SQLite FTS tables.
        
        Args:
            doc_id: Unique identifier for the document
            document: Document data to index
            
        Returns:
            True if successful, False otherwise
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                # For SQLite, we store the document in the kv_store and update FTS
                file_path = document.get('path', doc_id)
                content = document.get('content', '')
                
                # Store in kv_store
                conn.execute('''
                    INSERT OR REPLACE INTO kv_store (key, value, value_type)
                    VALUES (?, ?, 'text')
                ''', (doc_id, content.encode('utf-8')))
                
                # Update FTS tables if enabled
                if self.enable_fts:
                    # Update content FTS
                    conn.execute('''
                        INSERT OR REPLACE INTO kv_fts (key, value_text)
                        VALUES (?, ?)
                    ''', (doc_id, content))
                
                conn.commit()
                return True
        except Exception as e:
            logger.error(f"Error indexing document {doc_id}: {e}")
            return False

    def close(self) -> None:
        """Close the search backend."""
        pass

    def index_file(self, file_path: str, content: str) -> None:
        """Index a file for search.

        Args:
            file_path: Path of the file to index
            content: Content of the file to index

        Raises:
            IOError: If the file cannot be indexed
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                # Store content in kv_store
                conn.execute('''
                    INSERT OR REPLACE INTO kv_store (key, value, value_type)
                    VALUES (?, ?, 'text')
                ''', (file_path, content.encode('utf-8')))

                # Update FTS if enabled
                if self.enable_fts:
                    conn.execute('''
                        INSERT OR REPLACE INTO kv_fts (key, value_text)
                        VALUES (?, ?)
                    ''', (file_path, content))

                conn.commit()
        except Exception as e:
            logger.error(f"Error indexing file {file_path}: {e}")
            raise IOError(f"Failed to index file {file_path}: {e}")

    def delete_indexed_file(self, file_path: str) -> None:
        """Delete a file from the search index.

        Args:
            file_path: Path of the file to delete from index
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute('DELETE FROM kv_store WHERE key = ?', (file_path,))
                if self.enable_fts:
                    conn.execute('DELETE FROM kv_fts WHERE key = ?', (file_path,))
                conn.commit()
        except Exception as e:
            logger.error(f"Error deleting indexed file {file_path}: {e}")

    def search_files(self, query: str) -> List[Dict[str, Any]]:
        """Search for files matching the query.

        Args:
            query: The search query string

        Returns:
            A list of dictionaries containing file search results
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                if self.enable_fts:
                    cursor = conn.execute('''
                        SELECT key, value
                        FROM kv_fts
                        WHERE kv_fts MATCH ?
                        LIMIT 100
                    ''', (query,))
                else:
                    cursor = conn.execute('''
                        SELECT key, value
                        FROM kv_store
                        WHERE value_type = 'text' AND key LIKE ?
                        LIMIT 100
                    ''', (f'%{query}%',))

                results = []
                for key, value in cursor.fetchall():
                    results.append({
                        'path': key,
                        'content': value.decode('utf-8') if isinstance(value, bytes) else value
                    })
                return results
        except Exception as e:
            logger.error(f"Error searching files: {e}")
            return []

    def clear(self) -> bool:
        """Clear the search index.

        Returns:
            True if successful, False otherwise
        """
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute('DELETE FROM kv_store')
                if self.enable_fts:
                    conn.execute('DELETE FROM kv_fts')
                    conn.execute('DELETE FROM files_fts')
                conn.commit()
                return True
        except Exception as e:
            logger.error(f"Error clearing search index: {e}")
            return False


class SQLiteDAL(DALInterface):
    """
    SQLite implementation of the Data Access Layer (DAL) interface.
    This class aggregates the SQLite-specific storage, metadata, and search
    implementations.
    """
    def __init__(self, db_path: str, enable_fts: bool = True):
        self._storage = SQLiteStorage(db_path)
        self._metadata = SQLiteFileMetadata(db_path)
        self._search = SQLiteSearch(db_path, enable_fts=enable_fts)

    @property
    def storage(self) -> StorageInterface:
        return self._storage

    @property
    def metadata(self) -> FileMetadataInterface:
        return self._metadata

    @property
    def search(self) -> SearchInterface:
        return self._search

    def close(self) -> None:
        """
        Closes all underlying SQLite storage backends.
        """
        self._storage.close()
        self._metadata.close()
        self._search.close()

    def flush(self) -> None:
        """
        Flush all underlying SQLite storage backends to ensure data persistence.

        This method ensures all pending writes are synced to disk before closing.
        """
        self._storage.flush()
        self._metadata.flush()
        # SQLiteSearch doesn't have a flush method as it's read-only for queries
        # and writes are auto-committed

    def clear_all(self) -> bool:
        """
        Clears all data from all underlying SQLite storage backends.
        """
        storage_cleared = self._storage.clear()
        metadata_cleared = self._metadata.clear()
        # Note: SQLiteSearch does not have a clear method in the current implementation.
        # If it had data to clear, it would need to be added here.
        return storage_cleared and metadata_cleared
