"""Atomic table and index management utilities for tenant databases."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/15_utils_db.ipynb.

# %% auto #0
__all__ = ['logger', 'register_table', 'register_tables', 'drop_table', 'create_index', 'create_indexes', 'drop_index',
           'table_exists']

# %% ../nbs/15_utils_db.ipynb #8b74428b
from fastsql import *
from sqlalchemy import text
import logging
from typing import List, Dict, Any, Optional, Tuple, Type
from .utils_sql import get_db_type
from nbdev.showdoc import show_doc

# Module-level logger - configured by app via configure_logging()
logger = logging.getLogger(__name__)

# %% ../nbs/15_utils_db.ipynb #c6710b0f
def register_table(tenant_db: Database, model_class: Type, table_name: str, pk: str = 'id'):
    """Create a table from a dataclass model if it doesn't exist (atomic)."""
    try:
        table = tenant_db.create(model_class, name=table_name, pk=pk)
        tenant_db.conn.commit()
        return table
    except Exception as e:
        logger.error(f"Failed to create table '{table_name}': {e}", exc_info=True)
        tenant_db.conn.rollback()
        raise Exception(f"Failed to create table '{table_name}': {str(e)}") from e

# %% ../nbs/15_utils_db.ipynb #ad01cba0
def register_tables(tenant_db: Database, models: List[Tuple[Type, str, str]]) -> Dict[str, Any]:
    """Create multiple tables atomically (all succeed or all rollback)."""
    current_table = None
    try:
        tables = {}
        for model_class, table_name, pk in models:
            current_table = table_name
            tables[table_name] = tenant_db.create(model_class, name=table_name, pk=pk)
        tenant_db.conn.commit()
        return tables
    except Exception as e:
        logger.error(f"Failed to create table '{current_table}': {e}", exc_info=True)
        tenant_db.conn.rollback()
        raise Exception(f"Failed to create table '{current_table}': {str(e)}") from e

# %% ../nbs/15_utils_db.ipynb #40a18442
def drop_table(tenant_db: Database, table_name: str) -> None:
    """Drop a table if it exists (atomic operation)."""
    sql = f"DROP TABLE IF EXISTS {table_name}"
    try:
        tenant_db.conn.execute(text(sql))
        tenant_db.conn.commit()
    except Exception as e:
        logger.error(f"Failed to drop table '{table_name}': {e}", exc_info=True)
        tenant_db.conn.rollback()
        raise Exception(f"Failed to drop table '{table_name}': {str(e)}") from e

# %% ../nbs/15_utils_db.ipynb #1f3ff54e
def create_index(tenant_db: Database, table_name: str, columns: List[str], 
                 unique: bool = False, index_name: str = None) -> None:
    """Create an index on a table if it doesn't exist (atomic operation)."""
    # Auto-generate index name if not provided
    if index_name is None:
        index_name = f"idx_{table_name}_{'_'.join(columns)}"
    
    unique_clause = "UNIQUE " if unique else ""
    columns_clause = ", ".join(columns)
    
    # CREATE INDEX IF NOT EXISTS works for both PostgreSQL and SQLite
    sql = f"CREATE {unique_clause}INDEX IF NOT EXISTS {index_name} ON {table_name} ({columns_clause})"
    
    try:
        tenant_db.conn.execute(text(sql))
        tenant_db.conn.commit()
    except Exception as e:
        logger.error(f"Failed to create index '{index_name}' on '{table_name}': {e}", exc_info=True)
        tenant_db.conn.rollback()
        raise Exception(f"Failed to create index '{index_name}' on '{table_name}': {str(e)}") from e

# %% ../nbs/15_utils_db.ipynb #054bc1cc
def create_indexes(tenant_db: Database, indexes: List[Tuple[str, List[str], bool, Optional[str]]]) -> None:
    """Create multiple indexes atomically (all succeed or all rollback)."""
    current_index = None
    current_table = None
    try:
        for table_name, columns, unique, index_name in indexes:
            current_table = table_name
            # Auto-generate index name if not provided
            if index_name is None:
                index_name = f"idx_{table_name}_{'_'.join(columns)}"
            current_index = index_name
            
            unique_clause = "UNIQUE " if unique else ""
            columns_clause = ", ".join(columns)
            sql = f"CREATE {unique_clause}INDEX IF NOT EXISTS {index_name} ON {table_name} ({columns_clause})"
            tenant_db.conn.execute(text(sql))
        
        tenant_db.conn.commit()
    except Exception as e:
        logger.error(f"Failed to create index '{current_index}' on '{current_table}': {e}", exc_info=True)
        tenant_db.conn.rollback()
        raise Exception(f"Failed to create index '{current_index}' on '{current_table}': {str(e)}") from e

# %% ../nbs/15_utils_db.ipynb #c9a63d6f
def drop_index(tenant_db: Database, index_name: str, table_name: str = None) -> None:
    """Drop an index if it exists (atomic operation)."""
    db_type = get_db_type()
    
    if db_type == "POSTGRESQL":
        # PostgreSQL: DROP INDEX IF EXISTS index_name
        sql = f"DROP INDEX IF EXISTS {index_name}"
    else:
        # SQLite: DROP INDEX IF EXISTS index_name
        sql = f"DROP INDEX IF EXISTS {index_name}"
    
    try:
        tenant_db.conn.execute(text(sql))
        tenant_db.conn.commit()
    except Exception as e:
        logger.error(f"Failed to drop index '{index_name}': {e}", exc_info=True)
        tenant_db.conn.rollback()
        raise Exception(f"Failed to drop index '{index_name}': {str(e)}") from e

# %% ../nbs/15_utils_db.ipynb #79114fc8
def table_exists(tenant_db: Database, table_name: str) -> bool:
    """Check if a table exists in the database."""
    db_type = get_db_type()
    
    if db_type == "POSTGRESQL":
        sql = """
            SELECT EXISTS (
                SELECT FROM information_schema.tables 
                WHERE table_name = :table_name
            )
        """
    else:  # SQLite
        sql = """
            SELECT EXISTS (
                SELECT 1 FROM sqlite_master 
                WHERE type = 'table' AND name = :table_name
            )
        """
    
    try:
        result = tenant_db.conn.execute(text(sql), {"table_name": table_name})
        return result.scalar()
    except Exception as e:
        logger.error(f"Failed to check if table '{table_name}' exists: {e}", exc_info=True)
        raise Exception(f"Failed to check if table '{table_name}' exists: {str(e)}") from e
