import logging
import httpx
from uuid import UUID
from celery import shared_task
from datetime import datetime, timezone
from core.database import CoreSessionLocal
from core.config import settings
from .models import HelpArticle
from .schemas import HelpSyncRequest, HelpSyncResponse

logger = logging.getLogger(__name__)

@shared_task(name="sync_all_articles_task")
def sync_all_articles_task():
    """
    Tarea periódica que recorre todos los artículos locales y los sincroniza con el Central.
    Solo se ejecuta si hay un CENTRAL_SERVER_URL configurado (Rol: Cliente/Spoke).
    """
    if not settings.CENTRAL_SERVER_URL:
        logger.info("Skipping sync: No CENTRAL_SERVER_URL configured (Hub mode).")
        return
    db = CoreSessionLocal()
    try:
        articles = db.query(HelpArticle).all()
        for article in articles:
            sync_single_article(article.uuid)
    except Exception as e:
        logger.error(f"Error in sync_all_articles_task: {e}")
    finally:
        db.close()

@shared_task(name="sync_single_article_task")
def sync_single_article_task(article_uuid_str: str):
    """
    Sincroniza un único artículo inmediatamente después de una edición local.
    """
    sync_single_article(article_uuid_str)


@shared_task(name="broadcast_help_update")
def broadcast_help_update(article_uuid_str: str):
    """
    Difunde una actualización de artículo a todos los spokes configurados.
    """
    if not settings.SPOKE_URLS:
        logger.info("No SPOKE_URLS configured. Skipping broadcast.")
        return

    spokes = [s.strip() for s in settings.SPOKE_URLS.split(",") if s.strip()]
    headers = {"X-Sync-Token": settings.SYNC_SECRET_TOKEN}
    
    db = CoreSessionLocal()
    try:
        article = db.query(HelpArticle).filter(HelpArticle.uuid == article_uuid_str).first()
        if not article:
            logger.error(f"Article {article_uuid_str} not found for broadcast.")
            return

        sync_payload = HelpSyncRequest(
            article_uuid=article.uuid,
            client_updated_at=article.updated_at,
            client_content=article.content,
            client_title=article.title,
            client_slug=article.slug,
            last_editor=article.last_editor,
            client_category=article.category,
            client_order=article.order
        ).model_dump(mode='json')

        with httpx.Client() as client:
            for spoke_url in spokes:
                # Loop Prevention: Skip if the spoke is the origin
                try:
                    logger.info(f"Broadcasting update to {spoke_url}")
                    response = client.post(
                        spoke_url,
                        json=sync_payload,
                        headers=headers,
                        timeout=5.0
                    )
                    if response.status_code != 200:
                        logger.warning(f"Broadcast to {spoke_url} failed: {response.status_code}")
                except Exception as e:
                    logger.error(f"Error broadcasting to {spoke_url}: {e}")

    except Exception as e:
        logger.error(f"Broadcast error: {e}")
    finally:
        db.close()


def sync_single_article(article_uuid):
    """
    Lógica compartida para sincronizar un artículo con el servidor central.
    """
    logger.info(f"DEBUG: Syncing article {article_uuid}. CENTRAL_SERVER_URL='{settings.CENTRAL_SERVER_URL}' (Type: {type(settings.CENTRAL_SERVER_URL)})")
    
    if not settings.CENTRAL_SERVER_URL or settings.CENTRAL_SERVER_URL == '""':
        # Enhanced check to catch literal empty quotes if they slip through
        return

    db = CoreSessionLocal()
    try:
        article = db.query(HelpArticle).filter(HelpArticle.uuid == article_uuid).first()
        if not article:
            return

        sync_data = HelpSyncRequest(
            article_uuid=article.uuid,
            client_updated_at=article.updated_at,
            client_content=article.content,
            client_title=article.title,
            client_slug=article.slug,
            last_editor=article.last_editor,
            client_category=article.category,
            client_order=article.order
        )

        headers = {"X-Sync-Token": settings.SYNC_SECRET_TOKEN}
        
        with httpx.Client() as client:
            response = client.post(
                settings.CENTRAL_SERVER_URL,
                json=sync_data.model_dump(mode='json'),
                headers=headers,
                timeout=10.0
            )

            if response.status_code == 200:
                result = HelpSyncResponse(**response.json())
                if result.status == "UPDATE_REQUIRED":
                    # El servidor tiene una versión más nueva, actualizamos localmente
                    article.content = result.server_content
                    article.title = result.server_title
                    article.slug = result.server_slug
                    article.updated_at = result.server_updated_at
                    db.commit()
                    logger.info(f"Article {article.uuid} updated from server.")
                    
                    # Download assets if needed
                    from .utils import download_file_from_hub, sync_assets_from_content
                    if result.server_file_url:
                        download_file_from_hub(result.server_file_url)
                    sync_assets_from_content(result.server_content)
                else:
                    logger.info(f"Article {article.uuid} sync OK: {result.message}")
            else:
                logger.error(f"Sync failed for article {article.uuid}: {response.status_code} - {response.text}")

    except Exception as e:
        logger.error(f"Error syncing article {article.uuid}: {e}")
    finally:
        db.close()

from sqlalchemy import func

@shared_task(name="sync_from_hub_task")
def sync_from_hub_task():
    """
    Tarea de POLLING que el Cliente ejecuta periódicamente.
    Consulta al Hub (CENTRAL_SERVER_URL) por artículos modificados desde
    la última actualización local.
    """
    if not settings.CENTRAL_SERVER_URL:
        return

    db = CoreSessionLocal()
    try:
        # 1. Obtener la fecha de la última actualización local
        last_local_update = db.query(func.max(HelpArticle.updated_at)).scalar()
        if not last_local_update:
             # Si no hay datos, traer todo desde el principio de los tiempos
            last_local_update = datetime(2000, 1, 1, tzinfo=timezone.utc)
        
        # Asegurar timezone awareness
        if last_local_update.tzinfo is None:
            last_local_update = last_local_update.replace(tzinfo=timezone.utc)
            
        logger.info(f"Polling Hub for updates since {last_local_update}")

        # 2. Consultar al Hub
        headers = {"X-Sync-Token": settings.SYNC_SECRET_TOKEN}
        # CENTRAL_SERVER_URL es ".../help-center/sync/"
        # Queremos ".../help-center/modifications/"
        hub_url = settings.CENTRAL_SERVER_URL.replace("/sync/", "/modifications/")
        
        with httpx.Client() as client:
            response = client.get(
                hub_url,
                params={"since": last_local_update.isoformat()},
                headers=headers,
                timeout=10.0
            )

            if response.status_code == 200:
                articles_data = response.json()
                if not articles_data:
                    logger.info("No updates found.")
                    return

                logger.info(f"Found {len(articles_data)} updates from Hub. Applying...")
                
                # 3. Aplicar actualizaciones
                for art_data in articles_data:
                    try:
                        # Logic similar to sync_article but simpler (Force Update from Hub)
                        # We assume Hub is Truth in this Polling flow
                        
                        # Try to find by UUID
                        local_article = db.query(HelpArticle).filter(HelpArticle.uuid == art_data['uuid']).first()
                        
                        # Fallback: find by Slug if UUID doesn't match
                        if not local_article:
                            local_article = db.query(HelpArticle).filter(HelpArticle.slug == art_data['slug']).first()
                        
                        server_updated_at = datetime.fromisoformat(art_data['updated_at'])
                        if server_updated_at.tzinfo is None:
                            server_updated_at = server_updated_at.replace(tzinfo=timezone.utc)

                        if not local_article:
                            new_article = HelpArticle(
                                uuid=art_data['uuid'],
                                slug=art_data['slug'],
                                title=art_data['title'],
                                content=art_data['content'],
                                updated_at=server_updated_at,
                                last_editor=art_data['last_editor'],
                                category=art_data.get('category', "General"),
                                order=art_data.get('order', 0)
                            )
                            db.add(new_article)
                            logger.info(f"Created new article: {art_data['slug']}")
                        else:
                            # Update existing article
                            # If UUID changed in Hub but slug is the same, we update UUID too
                            local_article.uuid = art_data['uuid']
                            local_article.slug = art_data['slug']
                            local_article.title = art_data['title']
                            local_article.content = art_data['content']
                            local_article.updated_at = server_updated_at
                            local_article.last_editor = art_data['last_editor']
                            local_article.category = art_data.get('category', "General")
                            local_article.order = art_data.get('order', 0)
                            logger.info(f"Updated article: {art_data['slug']}")
                    
                        db.commit() # Commit each article to avoid bulk failure
                    except Exception as e:
                        db.rollback()
                        logger.error(f"Error syncing article {art_data.get('slug', 'unknown')}: {e}")


                # Download assets after bulk update (Polling)
                from .utils import download_file_from_hub, sync_assets_from_content
                for art_data in articles_data:
                    # art_data contains the virtual fields because it was dumped via HelpArticleInDB
                    if "file_url" in art_data and art_data['file_url']:
                        download_file_from_hub(art_data['file_url'])
                    
                    sync_assets_from_content(art_data.get('content', ''))

                logger.info("Polling sync completed successfully.")

            else:
                logger.error(f"Polling failed: {response.status_code} - {response.text}")

    except Exception as e:
        logger.error(f"Error in sync_from_hub_task: {e}")
    finally:
        db.close()
