from abc import ABC, abstractmethod
from typing import Optional, List, Dict, Any
import psycopg2
from psycopg2.extras import RealDictCursor
from contextlib import contextmanager
import logging

logger = logging.getLogger(__name__)

class BaseRepository(ABC):
    """Base repository with common database operations"""
    
    def __init__(self, db_url: str):
        self.db_url = db_url
    
    @contextmanager
    def get_db_connection(self):
        """Get database connection with automatic cleanup"""
        conn = None
        try:
            conn = psycopg2.connect(self.db_url)
            yield conn
        except Exception as e:
            if conn:
                conn.rollback()
            logger.error(f"Database error: {e}")
            raise
        finally:
            if conn:
                conn.close()
    
    @contextmanager
    def get_db_cursor(self, conn):
        """Get database cursor with dict results"""
        cursor = None
        try:
            cursor = conn.cursor(cursor_factory=RealDictCursor)
            yield cursor
        finally:
            if cursor:
                cursor.close()
    
    def execute_query(self, query: str, params: tuple = None) -> List[Dict[str, Any]]:
        """Execute SELECT query and return results"""
        with self.get_db_connection() as conn:
            with self.get_db_cursor(conn) as cursor:
                cursor.execute(query, params)
                return cursor.fetchall()
    
    def execute_one(self, query: str, params: tuple = None) -> Optional[Dict[str, Any]]:
        """Execute query and return single result"""
        with self.get_db_connection() as conn:
            with self.get_db_cursor(conn) as cursor:
                cursor.execute(query, params)
                return cursor.fetchone()
    
    def execute_insert(self, query: str, params: tuple = None) -> Optional[str]:
        """Execute INSERT query and return ID"""
        with self.get_db_connection() as conn:
            with self.get_db_cursor(conn) as cursor:
                cursor.execute(query + " RETURNING id", params)
                result = cursor.fetchone()
                conn.commit()
                return result['id'] if result else None
    
    def execute_update(self, query: str, params: tuple = None) -> int:
        """Execute UPDATE query and return affected rows"""
        with self.get_db_connection() as conn:
            with self.get_db_cursor(conn) as cursor:
                cursor.execute(query, params)
                affected = cursor.rowcount
                conn.commit()
                return affected
    
    def execute_delete(self, query: str, params: tuple = None) -> int:
        """Execute DELETE query and return affected rows"""
        with self.get_db_connection() as conn:
            with self.get_db_cursor(conn) as cursor:
                cursor.execute(query, params)
                affected = cursor.rowcount
                conn.commit()
                return affected
    
    def execute_batch(self, queries: List[tuple]) -> None:
        """Execute multiple queries in a transaction"""
        with self.get_db_connection() as conn:
            with self.get_db_cursor(conn) as cursor:
                try:
                    for query, params in queries:
                        cursor.execute(query, params)
                    conn.commit()
                except Exception as e:
                    conn.rollback()
                    raise