"""Database service for MongoDB operations."""

import logging
from motor.motor_asyncio import AsyncIOMotorClient
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
from typing import List, Dict, Optional, Any
from datetime import datetime

from ..config.settings import MONGODB_URL, DATABASE_NAME, COLLECTION_NAME
from ..models.schemas import ScrapeResult, ScrapeStatus

import json
from bson import ObjectId

class JSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)
        return super().default(obj)

def serialize_doc(doc):
    """Convert MongoDB document to JSON serializable format"""
    if doc is None:
        return None
    
    # Handle ObjectId fields
    if '_id' in doc:
        doc['_id'] = str(doc['_id'])
    
    # Convert other ObjectId fields
    for key, value in doc.items():
        if isinstance(value, ObjectId):
            doc[key] = str(value)
    
    return doc


logger = logging.getLogger(__name__)

class DatabaseService:
    """Database service for MongoDB operations."""
    
    def __init__(self):
        self.client: Optional[AsyncIOMotorClient] = None
        self.database = None
        self.collection = None
        self.sync_client: Optional[MongoClient] = None
        
    async def connect(self):
        """Initialize MongoDB connection."""
        try:
            self.client = AsyncIOMotorClient(MONGODB_URL)
            self.database = self.client[DATABASE_NAME]
            self.collection = self.database[COLLECTION_NAME]
            
            # Test connection
            await self.client.admin.command('ping')
            logger.info("Database connected successfully")
            
        except Exception as e:
            logger.error(f"Database connection failed: {e}")
            raise
    
    def connect_sync(self):
        """Initialize synchronous MongoDB connection."""
        try:
            self.sync_client = MongoClient(MONGODB_URL)
            self.sync_client.admin.command('ping')
            logger.info("Sync database connected successfully")
            
        except Exception as e:
            logger.error(f"Sync database connection failed: {e}")
            raise
    
    async def disconnect(self):
        """Close database connection."""
        if self.client:
            self.client.close()
            logger.info("Database disconnected")
    
    async def insert_result(self, result: ScrapeResult) -> str:
        """Insert scrape result into database."""
        try:
            document = result.dict()
            await self.collection.insert_one(document)
            logger.info(f"Result inserted for task_id: {result.task_id}")
            return result.task_id
            
        except Exception as e:
            logger.error(f"Failed to insert result: {e}")
            raise
    
    async def get_result(self, task_id: str) -> Optional[Dict[str, Any]]:
        """Get scrape result by task_id."""
        try:
            result = await self.collection.find_one({"task_id": task_id})
            return serialize_doc(result) if result else None
            
        except Exception as e:
            logger.error(f"Failed to get result: {e}")
            raise
            
        except Exception as e:
            logger.error(f"Failed to get result: {e}")
            raise
    
    async def update_status(self, task_id: str, status: ScrapeStatus, 
                          results: Optional[List[Dict]] = None):
        """Update task status."""
        try:
            update_data = {
                "status": status.value,
                "updated_at": datetime.now()
            }
            
            if results:
                update_data["results"] = results
            
            await self.collection.update_one(
                {"task_id": task_id},
                {"$set": update_data}
            )
            
            logger.info(f"Status updated for task_id: {task_id}")
            
        except Exception as e:
            logger.error(f"Failed to update status: {e}")
            raise
    
    async def delete_results(self, query: Optional[str] = None) -> int:
        """Delete results from database."""
        try:
            if query:
                filter_query = {"query": {"$regex": query, "$options": "i"}}
            else:
                filter_query = {}
            
            result = await self.collection.delete_many(filter_query)
            logger.info(f"Deleted {result.deleted_count} results")
            return result.deleted_count
            
        except Exception as e:
            logger.error(f"Failed to delete results: {e}")
            raise
    
    async def get_all_results(self, limit: int = 100) -> List[Dict[str, Any]]:
        """Get all results with pagination."""
        try:
            cursor = self.collection.find().limit(limit).sort("timestamp", -1)
            results = await cursor.to_list(length=limit)
            return [serialize_doc(doc) for doc in results]
            
        except Exception as e:
            logger.error(f"Failed to get all results: {e}")
            raise
            
        except Exception as e:
            logger.error(f"Failed to get all results: {e}")
            raise

# Global database instance
db_service = DatabaseService()
