"""
Database Connection - Supports both SQLite (dev) and PostgreSQL (production).

Uses aiosqlite for SQLite and asyncpg for PostgreSQL based on DATABASE_URL.
"""

import os
from typing import Optional, Any
from contextlib import asynccontextmanager
import structlog

logger = structlog.get_logger(__name__)


class Database:
    """Async database connection manager."""
    
    def __init__(self, database_url: Optional[str] = None):
        self.database_url = database_url or os.getenv(
            'DATABASE_URL',
            'sqlite:///./dev.db'
        )
        self.is_sqlite = self.database_url.startswith('sqlite')
        self.pool: Optional[Any] = None
        self.connection: Optional[Any] = None
    
    async def connect(self):
        """Create connection pool."""
        if self.pool or self.connection:
            return
        
        logger.info("database_connecting", url=self.database_url, type='sqlite' if self.is_sqlite else 'postgresql')
        
        try:
            if self.is_sqlite:
                # Use aiosqlite for SQLite
                import aiosqlite
                db_path = self.database_url.replace('sqlite:///', '')
                self.connection = await aiosqlite.connect(db_path)
                # Enable foreign keys
                await self.connection.execute("PRAGMA foreign_keys = ON")
                logger.info("database_connected", type="sqlite")
            else:
                # Use asyncpg for PostgreSQL
                import asyncpg
                self.pool = await asyncpg.create_pool(
                    self.database_url,
                    min_size=2,
                    max_size=10,
                    command_timeout=60
                )
                logger.info("database_connected", type="postgresql")
        except Exception as e:
            logger.error("database_connection_failed", error=str(e))
            raise
    
    async def disconnect(self):
        """Close connection pool."""
        if self.is_sqlite:
            if self.connection:
                await self.connection.close()
                self.connection = None
        else:
            if self.pool:
                await self.pool.close()
                self.pool = None
        logger.info("database_disconnected")
    
    @asynccontextmanager
    async def acquire(self):
        """Acquire a connection."""
        if not (self.pool or self.connection):
            await self.connect()
        
        if self.is_sqlite:
            yield self.connection
        else:
            async with self.pool.acquire() as connection:
                yield connection
    
    async def execute(self, query: str, *args):
        """Execute a query."""
        async with self.acquire() as conn:
            if self.is_sqlite:
                await conn.execute(query, args)
                await conn.commit()
            else:
                return await conn.execute(query, *args)
    
    async def fetch(self, query: str, *args):
        """Fetch multiple rows."""
        async with self.acquire() as conn:
            if self.is_sqlite:
                cursor = await conn.execute(query, args)
                rows = await cursor.fetchall()
                # Convert to dict-like objects
                return [dict(row) for row in rows]
            else:
                return await conn.fetch(query, *args)
    
    async def fetchrow(self, query: str, *args):
        """Fetch a single row."""
        async with self.acquire() as conn:
            if self.is_sqlite:
                cursor = await conn.execute(query, args)
                row = await cursor.fetchone()
                return dict(row) if row else None
            else:
                return await conn.fetchrow(query, *args)
    
    async def fetchval(self, query: str, *args):
        """Fetch a single value."""
        async with self.acquire() as conn:
            if self.is_sqlite:
                cursor = await conn.execute(query, args)
                row = await cursor.fetchone()
                return row[0] if row else None
            else:
                return await conn.fetchval(query, *args)


# Global database instance
db = Database()
