import sys
from typing import Any

from sqlalchemy import MetaData, Table, insert, select, text, update

from connection import DBConnectionHandler
from logger_config import logger


class BaseRepository:
    """
    Classe base para acesso a dados utilizando SQLAlchemy Core.

    Pode operar em modo DEBUG, onde nenhuma conexão com o banco é realizada.
    """

    TABLE_NAME: str = "CHANGE_ME"

    def __init__(self) -> None:
        self._metadata = MetaData()
        self.debug = False

    def _log_debug_disabled(self) -> None:
        method_name = sys._getframe(1).f_code.co_name
        logger.warning(
            f"Acesso ao banco de dados desativado para o metodo: {method_name}"
        )

    def _get_table(self, engine, table_name: str) -> Table:
        """
        Carrega dinamicamente a tabela definida em TABLE_NAME.
        """
        return Table(
            table_name,
            self._metadata,
            autoload_with=engine,
        )

    def fetch_one_by_id(self, column_name: str, value: Any) -> dict[str, Any] | None:
        """
        Busca um único registro baseado em uma coluna identificadora.
        """
        if self.debug:
            self._log_debug_disabled()
            return None

        with DBConnectionHandler() as db:
            table = self._get_table(db.get_engine(), table_name=self.TABLE_NAME)

            stmt = select(table).where(getattr(table.c, column_name) == value)
            result = db.session.execute(stmt).mappings().first() if db.session else None

            return dict(result) if result else None

    def fetch_all_by_column(self, column_name: str, value: Any) -> list[dict[str, Any]]:
        """
        Retorna múltiplos registros filtrando por uma coluna específica.
        """
        if self.debug:
            self._log_debug_disabled()
            return []

        with DBConnectionHandler() as db:
            table = self._get_table(db.get_engine(), table_name=self.TABLE_NAME)

            stmt = select(table).where(getattr(table.c, column_name) == value)
            result = db.session.execute(stmt).mappings().all() if db.session else []

            return [dict(row) for row in result]

    def exists(self, column_name: str, value: Any) -> bool:
        """
        Verifica se existe ao menos um registro com o valor informado.
        """
        if self.debug:
            self._log_debug_disabled()
            return False

        with DBConnectionHandler() as db:
            table = self._get_table(db.get_engine(), table_name=self.TABLE_NAME)

            stmt = select(getattr(table.c, column_name)).where(
                getattr(table.c, column_name) == value
            )

            return db.session.execute(stmt).first() is not None if db.session else False

    def insert(self, payload: dict[str, Any]) -> None:
        """
        Insere um novo registro na tabela.
        """
        if self.debug:
            self._log_debug_disabled()
            return None

        with DBConnectionHandler() as db:
            if not db.session:
                return None

            table = self._get_table(db.get_engine(), table_name=self.TABLE_NAME)

            stmt = insert(table).values(payload)
            db.session.execute(stmt)
            db.session.commit()

            logger.info(f"Insert executed successfully | table={self.TABLE_NAME}")

    def update_by_column(
        self,
        filter_column: str,
        filter_value: Any,
        payload: dict[str, Any],
    ) -> None:
        """
        Atualiza registros com base em um filtro simples.
        """
        if self.debug:
            self._log_debug_disabled()
            return None

        with DBConnectionHandler() as db:
            if not db.session:
                return None

            table = self._get_table(db.get_engine(), table_name=self.TABLE_NAME)

            stmt = (
                update(table)
                .where(getattr(table.c, filter_column) == filter_value)
                .values(payload)
            )

            db.session.execute(stmt, execution_options={"synchronize_session": False})
            db.session.commit()

            logger.info(
                f"Update executed | table={self.TABLE_NAME} | filter={filter_column}"
            )

    def delete_by_column(self, column_name: str, value: Any) -> None:
        """
        Remove registros com base em uma coluna.
        """
        if self.debug:
            self._log_debug_disabled()
            return None

        with DBConnectionHandler() as db:
            if not db.session:
                return None

            table = self._get_table(db.get_engine(), table_name=self.TABLE_NAME)

            stmt = table.delete().where(getattr(table.c, column_name) == value)
            db.session.execute(stmt)
            db.session.commit()

            logger.warning(
                f"Delete executed | table={self.TABLE_NAME} | column={column_name}"
            )

    def execute_raw_query(
        self, sql: str, params: dict[str, Any] | None = None
    ) -> list[Any]:
        """
        Executa uma query SQL manual usando text().
        """
        if self.debug:
            self._log_debug_disabled()
            return []

        with DBConnectionHandler() as db:
            if not db.session:
                return []

            stmt = text(sql)
            result = db.session.execute(stmt, params or {})
            return result.fetchall()  # type: ignore
