"""
Database Abstraction Layer
Standalone version for cubesquare package.

Provides backend-agnostic database interface.
"""

from abc import ABC, abstractmethod
from typing import Dict, List, Any, Optional
from datetime import datetime
from enum import Enum
import logging
import json


class DatabaseBackend(Enum):
    """Supported database backends."""
    SQLITE = "sqlite"
    POSTGRESQL = "postgresql"
    MYSQL = "mysql"


class DatabaseError(Exception):
    """Base exception for database operations."""
    pass


class ConnectionError(DatabaseError):
    """Database connection errors."""
    pass


class QueryError(DatabaseError):
    """Database query errors."""
    pass


class AbstractDatabaseAdapter(ABC):
    """Abstract database adapter interface."""

    def __init__(self, connection_string: str, **kwargs):
        """Initialize database adapter."""
        self.connection_string = connection_string
        self.connection = None
        self.logger = logging.getLogger(self.__class__.__name__)
        self.options = kwargs

    @abstractmethod
    def connect(self) -> bool:
        """Connect to database."""
        pass

    @abstractmethod
    def disconnect(self):
        """Disconnect from database."""
        pass

    @abstractmethod
    def execute(self, query: str, params: Optional[tuple] = None) -> Any:
        """Execute a query."""
        pass

    @abstractmethod
    def execute_many(self, query: str, params_list: List[tuple]) -> int:
        """Execute a query multiple times."""
        pass

    @abstractmethod
    def fetch_one(self, query: str, params: Optional[tuple] = None) -> Optional[Dict[str, Any]]:
        """Fetch a single row."""
        pass

    @abstractmethod
    def fetch_all(self, query: str, params: Optional[tuple] = None) -> List[Dict[str, Any]]:
        """Fetch all rows."""
        pass

    @abstractmethod
    def begin_transaction(self):
        """Begin a transaction."""
        pass

    @abstractmethod
    def commit(self):
        """Commit current transaction."""
        pass

    @abstractmethod
    def rollback(self):
        """Rollback current transaction."""
        pass

    @abstractmethod
    def get_backend(self) -> DatabaseBackend:
        """Get the database backend type."""
        pass

    @abstractmethod
    def get_last_insert_id(self) -> Optional[int]:
        """Get the ID of the last inserted row."""
        pass

    @abstractmethod
    def execute_script(self, sql_script: str):
        """Execute a multi-statement SQL script."""
        pass


class DatabaseAbstractionLayer:
    """High-level database abstraction layer."""

    def __init__(self, adapter: AbstractDatabaseAdapter):
        """Initialize database abstraction layer."""
        self.adapter = adapter
        self.logger = logging.getLogger(__name__)
        self._transaction_depth = 0

    def connect(self) -> bool:
        """Connect to database."""
        return self.adapter.connect()

    def disconnect(self):
        """Disconnect from database."""
        self.adapter.disconnect()

    def create_project(self, name: str, path: str, description: str = "",
                       metadata: Optional[Dict] = None) -> Optional[int]:
        """Create a new project."""
        try:
            now = datetime.now().isoformat()
            metadata_json = json.dumps(metadata) if metadata else None

            self.adapter.execute(
                """INSERT INTO projects (name, path, description, created_date,
                   modified_date, status, metadata)
                   VALUES (?, ?, ?, ?, ?, ?, ?)""",
                (name, path, description, now, now, 'active', metadata_json)
            )

            return self.adapter.get_last_insert_id()

        except Exception as e:
            self.logger.error(f"Failed to create project: {e}")
            return None

    def get_project(self, project_id: int) -> Optional[Dict[str, Any]]:
        """Get project by ID."""
        return self.adapter.fetch_one(
            "SELECT * FROM projects WHERE id = ?",
            (project_id,)
        )

    def list_projects(self, status: Optional[str] = None) -> List[Dict[str, Any]]:
        """List all projects."""
        if status:
            return self.adapter.fetch_all(
                "SELECT * FROM projects WHERE status = ? ORDER BY name",
                (status,)
            )
        else:
            return self.adapter.fetch_all(
                "SELECT * FROM projects ORDER BY name"
            )

    def transaction(self):
        """Context manager for transactions."""
        return DatabaseTransaction(self)


class DatabaseTransaction:
    """Context manager for database transactions."""

    def __init__(self, dal: DatabaseAbstractionLayer):
        self.dal = dal

    def __enter__(self):
        self.dal._transaction_depth += 1
        if self.dal._transaction_depth == 1:
            self.dal.adapter.begin_transaction()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            if self.dal._transaction_depth == 1:
                self.dal.adapter.commit()
        else:
            if self.dal._transaction_depth == 1:
                self.dal.adapter.rollback()

        self.dal._transaction_depth -= 1
        return False
