from typing import List, Optional
import logging
from fastapi import APIRouter, Depends, HTTPException, Query, status, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select

from app.api import deps
from app.models.product import Product
from app.schemas.product import (
    ProductCreate,
    ProductUpdate,
    ProductResponse,
    ProductListResponse,
)
from app.schemas.scraping import ScrapingResponse
from app.services.product_service import ProductService
from app.services.scraper_service import ScraperService

router = APIRouter()
logger = logging.getLogger(__name__)


async def _perform_background_scraping(
    product_id: int,
    headless_mode: bool,
    timeout_seconds: int,
    enable_discovery: bool,
):
    """
    Background task to perform scraping after product creation.
    This runs asynchronously without blocking the API response.
    """
    try:
        from app.db.session import AsyncSessionLocal
        
        logger.info(f"🚀 [Background] Starting automatic first-time scraping for product ID: {product_id}")
        
        async with AsyncSessionLocal() as db:
            scraping_result = await ScraperService.scrape_product_serp(
                db,
                product_id=product_id,
                enable_discovery=enable_discovery,
                headless_mode=headless_mode,
                timeout_seconds=timeout_seconds
            )
            
            logger.info(f"✅ [Background] First-time scraping completed for product ID: {product_id}")
            logger.info(f"[Background] Scraping Summary:")
            logger.info(f"   • Total Results: {scraping_result.get('total_results', 0)}")
            logger.info(f"   • Total Violations: {scraping_result.get('total_violations', 0)}")
            
    except Exception as e:
        logger.error(f"⚠️ [Background] Scraping failed for product ID {product_id}: {str(e)}", exc_info=True)


@router.get("/", response_model=ProductListResponse)
async def get_products(
    db: AsyncSession = Depends(deps.get_db),
    page: int = Query(1, ge=1),
    limit: int = Query(10, ge=1, le=100),
    sortBy: str = Query("product_name", regex="^(product_name|msp|last_scraped_date)$"),
    search: Optional[str] = None,
    current_user=Depends(deps.get_current_user),
):
    """
    Retrieve a list of products with pagination, sorting, and searching.
    """
    products, total = await ProductService.get_products(
        db, page=page, limit=limit, sort_by=sortBy, search=search
    )
    return ProductListResponse(items=products, total=total, page=page, limit=limit)


@router.post("/", response_model=dict, status_code=status.HTTP_201_CREATED)
async def add_product(
    product_in: ProductCreate,
    background_tasks: BackgroundTasks,
    headless: bool = Query(True, description="Run browser in headless mode for initial scraping"),
    timeout: int = Query(15, ge=5, le=60, description="Timeout in seconds for page load during initial scraping"),
    enable_discovery: bool = Query(True, description="Discover alternative vendors via SERP API during initial scraping"),
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
):
    """
    Add a new product and automatically trigger scraping in the background.
    
    **Returns immediately** (HTTP 201) without waiting for scraping to complete.
    Scraping happens asynchronously in the background using SERP API.
    
    Parameters:
    - product_in: Product creation data
    - headless: Whether to run browser in headless mode (default: True)
    - timeout: Page load timeout in seconds (default: 15)
    - enable_discovery: Discover alternative vendors via SERP API (default: True)
    
    **Response (Immediate):**
    - message: Product creation success
    - product_id: ID of created product
    - scraping_status: "in_progress" - indicates scraping is running in background
    
    **Note:** Scraping continues in background. You can check results via GET /products/{id} 
    or monitor logs for completion message: "First-time scraping completed for product ID: {id}"
    """
    # Create the product (immediate)
    product = await ProductService.create_product(db, product_in)
    logger.info(f"✅ Product created: {product.product_name} (ID: {product.id})")
    
    # Schedule scraping as background task (non-blocking)
    background_tasks.add_task(
        _perform_background_scraping,
        product_id=product.id,
        headless_mode=headless,
        timeout_seconds=timeout,
        enable_discovery=enable_discovery
    )
    logger.info(f"📌 Background scraping scheduled for product ID: {product.id}")
    
    # Return immediately to user
    return {
        "message": "Product added successfully! Scraping is starting in the background.",
        "product_id": product.id,
        "scraping_status": "in_progress",
        "note": "You can start working with the product while scraping happens. Check logs or GET /products/{id} for scraping results."
    }


@router.put("/{id}", response_model=dict)
async def update_product(
    id: int,
    product_in: ProductUpdate,
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
):
    """
    Edit an existing product. (Admin only)
    """
    await ProductService.update_product(db, id, product_in)
    return {"message": "Product updated successfully"}


@router.delete("/{id}", response_model=dict)
async def delete_product(
    id: int,
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
):
    """
    Delete a product. (Admin only)
    """
    await ProductService.delete_product(db, id)
    return {"message": "Product deleted successfully"}


@router.get("/{id}", response_model=ProductResponse)
async def get_product(
    id: int,
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
) -> ProductResponse:
    """
    Retrieve a single product by its ID.
    """
    product = await ProductService.get_product_by_id(db, id)
    return product


@router.post("/{id}/scrape", response_model=ScrapingResponse)
async def trigger_scraping(
    id: int,
    headless: bool = Query(True, description="Run browser in headless mode"),
    timeout: int = Query(15, ge=5, le=60, description="Timeout in seconds for page load"),
    enable_discovery: bool = Query(True, description="Also discover alternative vendors via Google search"),
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
):
    """
    Manually trigger scraping for a specific product.
    Scrapes the product against all registered active vendors.
    When discovery is enabled, also searches Google for alternative sellers and scrapes their prices.
    
    Parameters:
    - id: Product ID to scrape
    - headless: Whether to run browser in headless mode (default: True)
    - timeout: Page load timeout in seconds (default: 15)
    - enable_discovery: if true, discover and scrape alternative vendors (default: true)
    """
    product = await ProductService.get_product_by_id(db, id)
    if not product.status:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Scraping can only be triggered for active products",
        )
    
    # Run scraping service
    result = await ScraperService.scrape_product(
        db,
        product_id=id,
        enable_discovery=enable_discovery,
        headless_mode=headless,
        timeout_seconds=timeout
    )
    
    return result


@router.post("/scrape-all", response_model=dict, status_code=status.HTTP_200_OK)
async def scrape_all_products(
    headless: bool = Query(True, description="Run browser in headless mode"),
    timeout: int = Query(15, ge=5, le=60, description="Timeout in seconds for page load"),
    enable_discovery: bool = Query(True, description="Also discover alternative vendors via Google search"),
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
):
    """
    Manually trigger scraping for ALL active products.
    Scrapes EACH product against ALL registered active vendors.
    For each vendor's website URL: Extract price using search and price detection.
    Then if discovery enabled: Google search for alternative vendors and scrape them too.
    
    This is a SYNCHRONOUS operation - waits for all products to complete before returning.
    Products are scraped sequentially to avoid browser and connection issues.
    
    Parameters:
    - headless: Whether to run browser in headless mode (default: True)
    - timeout: Page load timeout in seconds (default: 15)
    - enable_discovery: Also discover and scrape alternative vendors via Google search (default: True)
    
    Returns:
    - Complete results including all scraped vendors and violations for each product
    """
    # Get all active products
    stmt = select(Product).where(Product.status == True)
    result = await db.execute(stmt)
    products = result.scalars().all()
    
    if not products:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No active products found for scraping"
        )
    
    all_products_results = []
    total_scraped = 0
    total_violations = 0
    failed_products = []
    
    logger.info(f"\n\n{'='*80}")
    logger.info(f"🚀 [SCRAPE ALL] Starting scraping for {len(products)} products")
    logger.info(f"{'='*80}\n")
    
    try:
        for idx, product in enumerate(products, 1):
            fresh_db = None
            try:
                # Create a fresh session for this product
                from app.db.session import AsyncSessionLocal
                fresh_db = AsyncSessionLocal()
                
                logger.info(f"\n{'─'*80}")
                logger.info(f"[{idx}/{len(products)}] 📦 PRODUCT: {product.product_name} (ID: {product.id})")
                logger.info(f"{'─'*80}")
                logger.info(f"        Barcode: {product.barcode}")
                logger.info(f"        MSP: ₹{product.msp}")
                logger.info(f"        Discovery Enabled: {enable_discovery}")
                
                # Scrape this product against all vendors + discovery
                product_result = await ScraperService.scrape_product(
                    fresh_db,
                    product_id=product.id,
                    enable_discovery=enable_discovery,
                    headless_mode=headless,
                    timeout_seconds=timeout
                )
                
                scraped_count = product_result.get("scraped_count", 0)
                violation_count = product_result.get("violation_count", 0)
                
                logger.info(f"\n✅ [RESULT] Product {product.id}:")
                logger.info(f"   • Vendors Scraped: {scraped_count}")
                logger.info(f"   • Violations Found: {violation_count}")
                
                # Log individual violations
                violations = product_result.get("violations", [])
                if violations:
                    logger.warning(f"\n   🚨 VIOLATIONS FOR {product.product_name}:")
                    for v in violations:
                        logger.warning(f"      - {v['vendor_name']}: ₹{v['scraped_price']:.3f} (MSP: ₹{v['msp']:.3f})")
                
                total_scraped += scraped_count
                total_violations += violation_count
                all_products_results.append(product_result)
                
            except HTTPException as he:
                logger.error(f"\n❌ [ERROR] Product {product.id}: {he.detail}", exc_info=False)
                failed_products.append({
                    "product_id": product.id,
                    "product_name": product.product_name,
                    "error": he.detail
                })
            except Exception as e:
                logger.error(f"\n❌ [ERROR] Product {product.id}: {str(e)}", exc_info=True)
                failed_products.append({
                    "product_id": product.id,
                    "product_name": product.product_name,
                    "error": str(e)
                })
            finally:
                # Ensure session is properly closed
                if fresh_db:
                    try:
                        await fresh_db.close()
                    except Exception as e:
                        logger.warning(f"Warning closing session: {str(e)}")
        
        # Final summary
        logger.info(f"\n\n{'='*80}")
        logger.info(f"📊 [SCRAPE ALL] FINAL SUMMARY")
        logger.info(f"{'='*80}")
        logger.info(f"Total Products: {len(products)}")
        logger.info(f"Successfully Scraped: {len(all_products_results)}")
        logger.info(f"Failed: {len(failed_products)}")
        logger.info(f"Total Vendor URLs Scraped: {total_scraped}")
        logger.info(f"Total Violations Found: {total_violations}")
        logger.info(f"{'='*80}\n")
        
        return {
            "status": "completed",
            "message": f"Scraping completed for {len(all_products_results)} products",
            "total_products": len(products),
            "successful_products": len(all_products_results),
            "failed_products": len(failed_products),
            "total_vendors_scraped": total_scraped,
            "total_violations": total_violations,
            "products": all_products_results,
            "failed_list": failed_products if failed_products else None,
            "enable_discovery": enable_discovery
        }
        
    except Exception as e:
        logger.error(f"\n\n❌ [FATAL ERROR] Scrape All failed: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Scraping failed: {str(e)}"
        )


@router.post("/{id}/scrape-serp", response_model=dict)
async def trigger_scraping_serp(
    id: int,
    headless: bool = Query(True, description="Run browser in headless mode"),
    timeout: int = Query(15, ge=5, le=60, description="Timeout in seconds for page load"),
    enable_discovery: bool = Query(True, description="Also discover alternative vendors via SERP API Google Shopping"),
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
):
    """
    Manually trigger scraping for a specific product using SERP API for discovery.
    Same functionality as /{id}/scrape, but uses SERP API Google Shopping Light Engine
    instead of Tavily API for discovering alternative vendors.
    
    Scrapes the product against all registered active vendors.
    When discovery is enabled, also searches SERP API Google Shopping for alternative sellers 
    and scrapes their prices.
    
    Parameters:
    - id: Product ID to scrape
    - headless: Whether to run browser in headless mode (default: True)
    - timeout: Page load timeout in seconds (default: 15)
    - enable_discovery: if true, discover and scrape alternative vendors via SERP API (default: true)
    """
    product = await ProductService.get_product_by_id(db, id)
    if not product.status:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Scraping can only be triggered for active products",
        )
    
    # Run scraping service with SERP API discovery
    result = await ScraperService.scrape_product_serp(
        db,
        product_id=id,
        enable_discovery=enable_discovery,
        headless_mode=headless,
        timeout_seconds=timeout
    )
    
    return result


@router.post("/scrape-all-serp", response_model=dict, status_code=status.HTTP_200_OK)
async def scrape_all_products_serp(
    headless: bool = Query(True, description="Run browser in headless mode"),
    timeout: int = Query(15, ge=5, le=60, description="Timeout in seconds for page load"),
    enable_discovery: bool = Query(True, description="Also discover alternative vendors via SERP API Google Shopping"),
    db: AsyncSession = Depends(deps.get_db),
    current_user=Depends(deps.get_current_user),
):
    """
    Manually trigger scraping for ALL active products using SERP API for discovery.
    Same functionality as /scrape-all, but uses SERP API Google Shopping Light Engine
    instead of Tavily API for discovering alternative vendors.
    
    Scrapes EACH product against ALL registered active vendors.
    For each vendor's website URL: Extract price using search and price detection.
    Then if discovery enabled: SERP API search for alternative vendors and scrape them too.
    
    This is a SYNCHRONOUS operation - waits for all products to complete before returning.
    Products are scraped sequentially to avoid browser and connection issues.
    
    Parameters:
    - headless: Whether to run browser in headless mode (default: True)
    - timeout: Page load timeout in seconds (default: 15)
    - enable_discovery: Also discover and scrape alternative vendors via SERP API (default: True)
    
    Returns:
    - Complete results including all scraped vendors and violations for each product
    """
    # Get all active products
    stmt = select(Product).where(Product.status == True)
    result = await db.execute(stmt)
    products = result.scalars().all()
    
    if not products:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No active products found for scraping"
        )
    
    all_products_results = []
    total_scraped = 0
    total_violations = 0
    failed_products = []
    
    logger.info(f"\n\n{'='*80}")
    logger.info(f"🚀 [SCRAPE ALL SERP] Starting scraping for {len(products)} products using SERP API")
    logger.info(f"{'='*80}\n")
    
    try:
        for idx, product in enumerate(products, 1):
            fresh_db = None
            try:
                # Create a fresh session for this product
                from app.db.session import AsyncSessionLocal
                fresh_db = AsyncSessionLocal()
                
                logger.info(f"\n{'─'*80}")
                logger.info(f"[{idx}/{len(products)}] 📦 PRODUCT: {product.product_name} (ID: {product.id})")
                logger.info(f"{'─'*80}")
                logger.info(f"        Barcode: {product.barcode}")
                logger.info(f"        MSP: ₹{product.msp}")
                logger.info(f"        Discovery Method: SERP API Google Shopping")
                logger.info(f"        Discovery Enabled: {enable_discovery}")
                
                # Scrape this product against all vendors with SERP API discovery
                product_result = await ScraperService.scrape_product_serp(
                    fresh_db,
                    product_id=product.id,
                    enable_discovery=enable_discovery,
                    headless_mode=headless,
                    timeout_seconds=timeout
                )
                
                total_results = product_result.get("total_results", 0)
                total_violation_count = product_result.get("total_violations", 0)
                
                logger.info(f"\n✅ [RESULT] Product {product.id}:")
                logger.info(f"   • Total Results: {total_results}")
                logger.info(f"   • Total Violations Found: {total_violation_count}")
                
                # Log individual violations
                violations = product_result.get("violations", [])
                if violations:
                    logger.warning(f"\n   🚨 VIOLATIONS FOR {product.product_name}:")
                    for v in violations:
                        logger.warning(f"      - {v.get('vendor_name', 'Unknown')}: ₹{v.get('scraped_price', 'N/A')} (MSP: ₹{v.get('msp', 'N/A')})")
                
                total_scraped += total_results
                total_violations += total_violation_count
                all_products_results.append(product_result)
                
            except HTTPException as he:
                logger.error(f"\n❌ [ERROR] Product {product.id}: {he.detail}", exc_info=False)
                failed_products.append({
                    "product_id": product.id,
                    "product_name": product.product_name,
                    "error": he.detail
                })
            except Exception as e:
                logger.error(f"\n❌ [ERROR] Product {product.id}: {str(e)}", exc_info=True)
                failed_products.append({
                    "product_id": product.id,
                    "product_name": product.product_name,
                    "error": str(e)
                })
            finally:
                # Ensure session is properly closed
                if fresh_db:
                    try:
                        await fresh_db.close()
                    except Exception as e:
                        logger.warning(f"Warning closing session: {str(e)}")
        
        # Final summary
        logger.info(f"\n\n{'='*80}")
        logger.info(f"📊 [SCRAPE ALL SERP] FINAL SUMMARY")
        logger.info(f"{'='*80}")
        logger.info(f"Total Products: {len(products)}")
        logger.info(f"Successfully Scraped: {len(all_products_results)}")
        logger.info(f"Failed: {len(failed_products)}")
        logger.info(f"Total Results: {total_scraped}")
        logger.info(f"Total Violations Found: {total_violations}")
        logger.info(f"Discovery Method: SERP API Google Shopping Light Engine")
        logger.info(f"{'='*80}\n")
        
        return {
            "status": "completed",
            "message": f"Scraping completed for {len(all_products_results)} products using SERP API",
            "total_products": len(products),
            "successful_products": len(all_products_results),
            "failed_products": len(failed_products),
            "total_results": total_scraped,
            "total_violations": total_violations,
            "products": all_products_results,
            "failed_list": failed_products if failed_products else None,
            "enable_discovery": enable_discovery,
            "discovery_method": "serp_api_google_shopping",
            "source_type": "browser_with_serp_api_discovery"
        }
        
    except Exception as e:
        logger.error(f"\n\n❌ [FATAL ERROR] Scrape All SERP failed: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Scraping failed: {str(e)}"
        )
