"""Database dialect abstraction layer for different database types."""
from abc import ABC, abstractmethod


class InsertStrategy(ABC):
    """Abstract base class for database-specific insert strategies."""

    @abstractmethod
    def get_ignore_clause(self, ignore, **kwargs):
        """Get the ignore clause for duplicate handling."""
        pass

    @abstractmethod
    def validate_bulk_insert(self, ignore, **kwargs):
        """Validate bulk insert parameters for the specific dialect."""
        pass


class MySQLInsertStrategy(InsertStrategy):
    """MySQL-specific insert strategy."""

    def get_ignore_clause(self, ignore, **kwargs):
        return 'IGNORE' if ignore else ''

    def validate_bulk_insert(self, ignore, **kwargs):
        # MySQL supports IGNORE natively, no additional validation needed
        pass


class SQLiteInsertStrategy(InsertStrategy):
    """SQLite-specific insert strategy."""

    def get_ignore_clause(self, ignore, **kwargs):
        return 'OR REPLACE' if ignore else ''

    def validate_bulk_insert(self, ignore, **kwargs):
        # SQLite supports OR REPLACE natively, no additional validation needed
        pass


class OracleInsertStrategy(InsertStrategy):
    """Oracle-specific insert strategy."""

    def get_ignore_clause(self, ignore, table=None, index=None, **kwargs):
        if not ignore:
            return ''

        if index is None and table is not None:
            # Try to find a unique index
            for i in table.__table__.indexes:
                if i.unique:
                    index = i.name
                    break

            if index is None and table.__table__.constraints:
                # Use the first constraint as fallback
                index = list(table.__table__.constraints)[0].name

        if index is None:
            raise ValueError("No unique index found for Oracle IGNORE clause")

        return f'/*+ IGNORE_ROW_ON_DUPKEY_INDEX ({table.__table__.name}, {index}) */'

    def validate_bulk_insert(self, ignore, **kwargs):
        # Oracle requires index specification for ignore functionality
        if ignore and kwargs.get('index') is None:
            # Will be handled in get_ignore_clause with table inspection
            pass


class MSSQLInsertStrategy(InsertStrategy):
    """Microsoft SQL Server-specific insert strategy."""

    def get_ignore_clause(self, ignore, **kwargs):
        return ''  # MSSQL uses IGNORE_DUP_KEY at table level

    def validate_bulk_insert(self, ignore, **kwargs):
        if ignore:
            raise ValueError(
                "For MSSQL, set 'IGNORE_DUP_KEY = ON' when creating the table "
                "and use ignore=False parameter for insert operations."
            )


class DefaultInsertStrategy(InsertStrategy):
    """Default insert strategy for unsupported databases."""

    def get_ignore_clause(self, ignore, **kwargs):
        return ''

    def validate_bulk_insert(self, ignore, **kwargs):
        if ignore:
            raise ValueError(f"Ignore functionality not supported for this database dialect")


class DialectFactory:
    """Factory for creating database-specific insert strategies."""

    _strategies = {
        'mysql': MySQLInsertStrategy,
        'sqlite': SQLiteInsertStrategy,
        'oracle': OracleInsertStrategy,
        'mssql': MSSQLInsertStrategy,
        'postgresql': DefaultInsertStrategy,  # Can be extended with specific strategy
    }

    @classmethod
    def get_strategy(cls, dialect_name):
        """Get the appropriate insert strategy for the database dialect."""
        strategy_class = cls._strategies.get(dialect_name.lower(), DefaultInsertStrategy)
        return strategy_class()

    @classmethod
    def register_strategy(cls, dialect_name, strategy_class):
        """Register a new strategy for a database dialect."""
        if not issubclass(strategy_class, InsertStrategy):
            raise TypeError("Strategy must inherit from InsertStrategy")
        cls._strategies[dialect_name.lower()] = strategy_class