from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, Boolean, DateTime, Text, inspect
from sqlalchemy.sql import select, insert, update, delete
from datetime import datetime
from typing import Optional, Dict, List, Any
import os


class DatabaseManager:
    def __init__(self, db_url: Optional[str] = None):
        if db_url is None:
            db_url = os.environ.get('DATABASE_URL', 'sqlite:///app.db')
        
        self.engine = create_engine(db_url)
        self.metadata = MetaData()
        self.tables = {}
    
    def _get_or_create_table(self, table_name: str):
        """Get existing table or create a new one dynamically"""
        if table_name in self.tables:
            return self.tables[table_name]
        
        inspector = inspect(self.engine)
        
        if inspector.has_table(table_name):
            table = Table(table_name, self.metadata, autoload_with=self.engine)
            self.tables[table_name] = table
            return table
        
        table = Table(
            table_name,
            self.metadata,
            Column('id', Integer, primary_key=True, autoincrement=True),
            Column('created_at', DateTime, default=datetime.utcnow),
            extend_existing=True
        )
        
        self.tables[table_name] = table
        self.metadata.create_all(self.engine)
        return table
    
    def save(self, table_name: str, data: dict):
        """Insert data into a table"""
        table = self._get_or_create_table(table_name)
        
        inspector = inspect(self.engine)
        existing_columns = {col['name'] for col in inspector.get_columns(table_name)}
        
        for key in data.keys():
            if key not in existing_columns and key != 'id':
                column_type = self._infer_column_type(data[key])
                with self.engine.begin() as conn:
                    conn.execute(f'ALTER TABLE {table_name} ADD COLUMN {key} {column_type}')
        
        table = Table(table_name, self.metadata, autoload_with=self.engine, extend_existing=True)
        self.tables[table_name] = table
        
        data['created_at'] = datetime.utcnow()
        
        with self.engine.begin() as conn:
            result = conn.execute(insert(table).values(**data))
            return result.inserted_primary_key[0]
    
    def query(self, table_name: str, where: Optional[Dict[str, Any]] = None, order_by: Optional[str] = None, limit: Optional[int] = None):
        """Query data from a table"""
        table = self._get_or_create_table(table_name)
        
        stmt = select(table)
        
        if where:
            for key, value in where.items():
                stmt = stmt.where(getattr(table.c, key) == value)
        
        if order_by:
            if order_by.upper().endswith(' DESC'):
                col_name = order_by.replace(' DESC', '').strip()
                stmt = stmt.order_by(getattr(table.c, col_name).desc())
            else:
                col_name = order_by.replace(' ASC', '').strip()
                stmt = stmt.order_by(getattr(table.c, col_name))
        
        if limit:
            stmt = stmt.limit(limit)
        
        with self.engine.connect() as conn:
            result = conn.execute(stmt)
            rows = result.fetchall()
            return [self._row_to_dict(row, table) for row in rows]
    
    def update(self, table_name: str, record_id: int, data: dict):
        """Update a record by ID"""
        table = self._get_or_create_table(table_name)
        
        with self.engine.begin() as conn:
            stmt = update(table).where(table.c.id == record_id).values(**data)
            conn.execute(stmt)
    
    def delete(self, table_name: str, record_id: int):
        """Delete a record by ID"""
        table = self._get_or_create_table(table_name)
        
        with self.engine.begin() as conn:
            stmt = delete(table).where(table.c.id == record_id)
            conn.execute(stmt)
    
    def _infer_column_type(self, value):
        """Infer SQL column type from Python value"""
        if isinstance(value, bool):
            return 'BOOLEAN'
        elif isinstance(value, int):
            return 'INTEGER'
        elif isinstance(value, float):
            return 'REAL'
        elif isinstance(value, datetime):
            return 'DATETIME'
        elif len(str(value)) > 255:
            return 'TEXT'
        else:
            return 'VARCHAR(255)'
    
    def _row_to_dict(self, row, table):
        """Convert SQLAlchemy row to dict with attribute access"""
        class Row:
            def __init__(self, data):
                self.__dict__.update(data)
            
            def __repr__(self):
                return f"Row({self.__dict__})"
        
        data = dict(row._mapping)
        return Row(data)
