"""
SQLite Database Adapter
Standalone version for cubesquare package.
"""

import sqlite3
from pathlib import Path
from typing import Dict, List, Any, Optional
import time

from .abstraction import (
    AbstractDatabaseAdapter,
    DatabaseBackend,
    DatabaseError,
    ConnectionError,
    QueryError
)


class SQLiteAdapter(AbstractDatabaseAdapter):
    """SQLite implementation of database adapter."""

    def __init__(self, db_path: str, **kwargs):
        """Initialize SQLite adapter."""
        super().__init__(db_path, **kwargs)
        self.db_path = Path(db_path)
        self.connection = None
        self.cursor = None

        self.timeout = kwargs.get('timeout', 30.0)
        self.check_same_thread = kwargs.get('check_same_thread', False)
        self.cached_statements = kwargs.get('cached_statements', 128)
        self._max_retries = kwargs.get('max_retries', 3)
        self._retry_delay = kwargs.get('retry_delay', 1.0)

        self._transaction_depth = 0
        self._savepoint_stack = []

    def is_connected(self) -> bool:
        """Check if connection is alive."""
        if not self.connection:
            return False
        try:
            self.connection.execute("SELECT 1")
            return True
        except sqlite3.Error:
            return False

    def connect(self) -> bool:
        """Connect to SQLite database."""
        for attempt in range(self._max_retries):
            try:
                if self.connection:
                    try:
                        self.connection.close()
                    except:
                        pass

                self.connection = sqlite3.connect(
                    str(self.db_path),
                    timeout=self.timeout,
                    isolation_level=None,
                    check_same_thread=self.check_same_thread,
                    cached_statements=self.cached_statements
                )
                self.connection.row_factory = sqlite3.Row
                self.cursor = self.connection.cursor()

                self.cursor.execute("PRAGMA busy_timeout = 30000")
                self.cursor.execute("PRAGMA journal_mode = WAL")

                self.logger.info(f"Connected to SQLite database: {self.db_path}")
                return True

            except sqlite3.Error as e:
                self.logger.warning(f"Connection attempt {attempt + 1} failed: {e}")
                if attempt < self._max_retries - 1:
                    time.sleep(self._retry_delay * (attempt + 1))
                else:
                    raise ConnectionError(f"SQLite connection failed: {e}")

    def disconnect(self):
        """Disconnect from SQLite database."""
        if self.connection:
            self.connection.close()
            self.connection = None
            self.cursor = None

    def _ensure_connection(self):
        """Ensure database connection is active."""
        if not self.is_connected():
            self.connect()

    def execute(self, query: str, params: Optional[tuple] = None) -> Any:
        """Execute a query."""
        for attempt in range(self._max_retries):
            try:
                self._ensure_connection()
                if params:
                    return self.cursor.execute(query, params)
                else:
                    return self.cursor.execute(query)

            except sqlite3.OperationalError as e:
                if any(msg in str(e).lower() for msg in ['database is locked', 'disk i/o error']):
                    if attempt < self._max_retries - 1:
                        time.sleep(self._retry_delay * (attempt + 1))
                        try:
                            self.disconnect()
                            self.connect()
                        except:
                            pass
                    else:
                        raise QueryError(f"SQLite query failed: {e}")
                else:
                    raise QueryError(f"SQLite query failed: {e}")

            except sqlite3.Error as e:
                raise QueryError(f"SQLite query failed: {e}")

    def execute_many(self, query: str, params_list: List[tuple]) -> int:
        """Execute a query multiple times."""
        try:
            self.cursor.executemany(query, params_list)
            return self.cursor.rowcount
        except sqlite3.Error as e:
            raise QueryError(f"SQLite batch execution failed: {e}")

    def fetch_one(self, query: str, params: Optional[tuple] = None) -> Optional[Dict[str, Any]]:
        """Fetch a single row."""
        try:
            self.execute(query, params)
            row = self.cursor.fetchone()
            return dict(row) if row else None
        except sqlite3.Error as e:
            raise QueryError(f"SQLite fetch failed: {e}")

    def fetch_all(self, query: str, params: Optional[tuple] = None) -> List[Dict[str, Any]]:
        """Fetch all rows."""
        try:
            self.execute(query, params)
            rows = self.cursor.fetchall()
            return [dict(row) for row in rows]
        except sqlite3.Error as e:
            raise QueryError(f"SQLite fetch failed: {e}")

    def begin_transaction(self):
        """Begin a transaction."""
        try:
            self._transaction_depth += 1
            if self._transaction_depth == 1:
                self.connection.execute("BEGIN TRANSACTION")
            else:
                savepoint_name = f"sp_{self._transaction_depth}_{id(self)}"
                self.connection.execute(f"SAVEPOINT {savepoint_name}")
                self._savepoint_stack.append(savepoint_name)
        except sqlite3.Error as e:
            self._transaction_depth -= 1
            raise DatabaseError(f"SQLite transaction failed: {e}")

    def commit(self):
        """Commit current transaction."""
        try:
            if self._transaction_depth == 0:
                return

            if self._transaction_depth == 1:
                self.connection.commit()
            else:
                savepoint_name = self._savepoint_stack.pop()
                self.connection.execute(f"RELEASE SAVEPOINT {savepoint_name}")

            self._transaction_depth -= 1

        except sqlite3.Error as e:
            raise DatabaseError(f"SQLite commit failed: {e}")

    def rollback(self):
        """Rollback current transaction."""
        try:
            if self._transaction_depth == 0:
                return

            if self._transaction_depth == 1:
                self.connection.rollback()
            else:
                savepoint_name = self._savepoint_stack.pop()
                self.connection.execute(f"ROLLBACK TO SAVEPOINT {savepoint_name}")
                self.connection.execute(f"RELEASE SAVEPOINT {savepoint_name}")

            self._transaction_depth -= 1

        except sqlite3.Error as e:
            raise DatabaseError(f"SQLite rollback failed: {e}")

    def get_backend(self) -> DatabaseBackend:
        """Get the database backend type."""
        return DatabaseBackend.SQLITE

    def get_last_insert_id(self) -> Optional[int]:
        """Get the ID of the last inserted row."""
        if self.cursor:
            return self.cursor.lastrowid
        return None

    def execute_script(self, sql_script: str):
        """Execute a multi-statement SQL script."""
        try:
            self.connection.executescript(sql_script)
        except sqlite3.Error as e:
            raise QueryError(f"SQLite script execution failed: {e}")

    def create_tables(self) -> bool:
        """Create basic database tables."""
        try:
            self.execute("""
                CREATE TABLE IF NOT EXISTS tokens (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    hex_id TEXT NOT NULL UNIQUE,
                    content TEXT,
                    basin INTEGER,
                    layer INTEGER,
                    created_date TEXT NOT NULL
                )
            """)

            self.execute("""
                CREATE TABLE IF NOT EXISTS token_relationships (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    source_hex TEXT NOT NULL,
                    target_hex TEXT NOT NULL,
                    rel_type TEXT,
                    strength REAL DEFAULT 1.0,
                    created_date TEXT NOT NULL
                )
            """)

            self.execute("""
                CREATE TABLE IF NOT EXISTS basins (
                    id INTEGER PRIMARY KEY,
                    name TEXT,
                    layer INTEGER,
                    description TEXT
                )
            """)

            self.commit()
            return True

        except sqlite3.Error as e:
            self.rollback()
            self.logger.error(f"Failed to create tables: {e}")
            return False


def create_sqlite_adapter(db_path: str) -> SQLiteAdapter:
    """Factory function to create and connect a SQLite adapter."""
    adapter = SQLiteAdapter(db_path)
    adapter.connect()
    adapter.create_tables()
    return adapter
