import mimetypes
import os
import shutil
import uuid
from datetime import datetime
from typing import List, Optional, Dict, Any
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Header, status, UploadFile, File
from fastapi.responses import Response
from sqlalchemy.orm import Session

from core.config import settings
from core.database import get_core_db
from core.s3_keys import (
    help_asset_key,
    help_public_api_path,
    help_s3_key_to_public_relative_path,
    system_help_object_key,
)
from core.storage_s3 import get_object_bytes, put_object_bytes
from core.security import get_current_user, has_role
from .schemas import HelpArticleInDB, HelpArticleCreate, HelpArticleUpdate, HelpSyncRequest, HelpSyncResponse
from .services import HelpCenterService
from .tasks import sync_single_article_task

router = APIRouter(prefix="/help-center", tags=["Help Center"])

def verify_sync_token(x_sync_token: str = Header(...)):
    if x_sync_token != settings.SYNC_SECRET_TOKEN:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid Sync Token"
        )

def trigger_sync_or_broadcast(article_uuid: UUID):
    """
    Helper function to handle synchronization logic.
    - If we are a Client (CENTRAL_SERVER_URL is set): Trigger upstream sync.
    - If we are the Hub (No CENTRAL_SERVER, but SPOKE_URLS set): Trigger broadcast.
    """
    import logging
    logger = logging.getLogger(__name__)
    
    try:
        logger.info(f"DEBUG: Triggering sync/broadcast for article {article_uuid}")
        logger.debug(f"DEBUG: CENTRAL_SERVER_URL='{settings.CENTRAL_SERVER_URL}' SPOKE_URLS='{settings.SPOKE_URLS}'")
        
        # 1. Upstream Sync (Client -> Hub)
        if settings.CENTRAL_SERVER_URL and settings.CENTRAL_SERVER_URL != '""':
            logger.info(f"DEBUG: Queueing sync_single_article_task for {article_uuid}")
            sync_single_article_task.delay(str(article_uuid))
        
        # 2. Downstream Broadcast (Hub -> Spokes)
        # Only if we are the Hub (no upstream) and have spokes configured.
        elif (not settings.CENTRAL_SERVER_URL or settings.CENTRAL_SERVER_URL == '""') and settings.SPOKE_URLS:
            from .tasks import broadcast_help_update
            logger.info(f"DEBUG: Queueing broadcast_help_update for {article_uuid}")
            # origin_client_uuid is None because this change originated on the Hub itself
            broadcast_help_update.delay(str(article_uuid), None)
        else:
            logger.info(f"DEBUG: No sync/broadcast needed for {article_uuid} (Config empty or Hub mode without spokes)")
            
    except Exception as e:
        logger.error(f"ERROR in trigger_sync_or_broadcast for article {article_uuid}: {str(e)}", exc_info=True)
        # We don't re-raise here to avoid returning 500 to the user if the save was successful

@router.post("/sync/", response_model=HelpSyncResponse, dependencies=[Depends(verify_sync_token)])
def sync_help_article(sync_data: HelpSyncRequest, db: Session = Depends(get_core_db)):
    """
    Endpoint de sincronización inteligente para artículos de ayuda.
    Requiere X-Sync-Token en los headers.
    """
    result = HelpCenterService.sync_article(db, sync_data)
    
    # Broadcast to other spokes (Hub logic)
    import logging
    logger = logging.getLogger(__name__)
    logger.info(f"DEBUG: Hub Sync Check. CENTRAL_SERVER_URL='{settings.CENTRAL_SERVER_URL}' SPOKE_URLS='{settings.SPOKE_URLS}'")
    
    if not settings.CENTRAL_SERVER_URL and settings.SPOKE_URLS:
        # We are the Hub (no central server to push to) and have Spokes configured
        from .tasks import broadcast_help_update
        logger.info(f"DEBUG: Triggering broadcast for article {sync_data.article_uuid}")
        broadcast_help_update.delay(
            str(sync_data.article_uuid), 
            str(sync_data.origin_client_uuid) if sync_data.origin_client_uuid else None
        )
    else:
        logger.info("DEBUG: Broadcast skipped (Condition failed)")
        
    return result

@router.get("/files/{file_path:path}")
def serve_help_file(file_path: str):
    """Sirve un objeto bajo system/help/ (público vía middleware)."""
    if ".." in file_path or file_path.startswith("/"):
        raise HTTPException(status_code=404, detail="Not found")
    try:
        key = system_help_object_key(file_path)
    except ValueError:
        raise HTTPException(status_code=404, detail="Not found")
    if not settings.use_s3_object_storage:
        legacy = os.path.join("uploads", "help", file_path)
        if not os.path.isfile(legacy):
            raise HTTPException(status_code=404, detail="Not found")
        with open(legacy, "rb") as f:
            data = f.read()
        media = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
        return Response(content=data, media_type=media)
    try:
        data = get_object_bytes(key)
    except Exception:
        raise HTTPException(status_code=404, detail="Not found")
    media = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
    return Response(content=data, media_type=media)


@router.post("/upload-image/")
async def upload_help_image(
    file: UploadFile = File(...),
    current_user: Dict[str, Any] = Depends(has_role("admin"))
):
    """Sube una imagen para usar en los artículos."""
    try:
        file_ext = os.path.splitext(file.filename or "")[1] or ".png"
        new_filename = f"{uuid.uuid4()}{file_ext}"
        body = await file.read()
        if settings.use_s3_object_storage:
            key = help_asset_key("", new_filename)
            ct = mimetypes.guess_type(new_filename)[0] or "image/png"
            put_object_bytes(key, body, content_type=ct)
            rel = help_s3_key_to_public_relative_path(key)
            return {"url": help_public_api_path(rel)}
        os.makedirs("uploads/help", exist_ok=True)
        file_location = f"uploads/help/{new_filename}"
        with open(file_location, "wb") as f:
            f.write(body)
        return {"url": f"/api/uploads/help/{new_filename}"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/upload-asset/")
async def upload_help_asset(
    file: UploadFile = File(...),
    current_user: Dict[str, Any] = Depends(has_role("admin"))
):
    """Sube cualquier tipo de archivo (PDF, Video, etc.) para la biblioteca."""
    try:
        file_ext = os.path.splitext(file.filename or "")[1].lower()
        new_filename = f"{uuid.uuid4()}{file_ext}"
        subfolder = "assets"
        if file_ext == ".pdf":
            subfolder = "pdfs"
        elif file_ext in [".mp4", ".mov", ".avi"]:
            subfolder = "videos"

        if settings.use_s3_object_storage:
            body = await file.read()
            key = help_asset_key(subfolder, new_filename)
            ct = file.content_type or mimetypes.guess_type(new_filename)[0] or "application/octet-stream"
            put_object_bytes(key, body, content_type=ct)
            rel = help_s3_key_to_public_relative_path(key)
            return {
                "url": help_public_api_path(rel),
                "filename": file.filename,
                "size": len(body),
                "mime_type": file.content_type,
            }

        folder = f"uploads/help/{subfolder}"
        os.makedirs(folder, exist_ok=True)
        file_location = f"{folder}/{new_filename}"
        body = await file.read()
        with open(file_location, "wb") as f:
            f.write(body)
        file_size = os.path.getsize(file_location)
        return {
            "url": f"/api/{file_location}",
            "filename": file.filename,
            "size": file_size,
            "mime_type": file.content_type,
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/articles/", response_model=List[HelpArticleInDB])
def list_articles(
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user)
):
    """Lista todos los artículos de ayuda."""
    return HelpCenterService.get_all(db)

@router.get("/modifications/", response_model=List[HelpArticleInDB], dependencies=[Depends(verify_sync_token)])
def get_modifications(since: datetime, db: Session = Depends(get_core_db)):
    """Obtiene artículos modificados desde la fecha indicada (Polling). Requiere X-Sync-Token."""
    return HelpCenterService.get_modifications(db, since)

@router.get("/articles/{article_uuid}/", response_model=HelpArticleInDB)
def get_article(
    article_uuid: UUID,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(get_current_user)
):
    """Obtiene un artículo por UUID."""
    article = HelpCenterService.get_by_uuid(db, article_uuid)
    if not article:
        raise HTTPException(status_code=404, detail="Article not found")
    return article

@router.post("/articles/", response_model=HelpArticleInDB, status_code=status.HTTP_201_CREATED)
def create_article(
    article: HelpArticleCreate,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(has_role("admin"))
):
    """Crea un nuevo artículo."""
    import logging
    logger = logging.getLogger(__name__)
    logger.info(f"DEBUG: Creating new article: {article.title} by {current_user.get('preferred_username')}")
    
    # Fill last_editor with admin username
    if current_user.get('preferred_username'):
        article.last_editor = current_user.get('preferred_username')
        
    new_article = HelpCenterService.create(db, article)
    logger.info(f"DEBUG: Article created successfully in DB. UUID: {new_article.uuid}")
    
    trigger_sync_or_broadcast(new_article.uuid)
    
    return new_article

@router.patch("/articles/{article_uuid}/", response_model=HelpArticleInDB)
def update_article(
    article_uuid: UUID,
    article_data: HelpArticleUpdate,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(has_role("admin"))
):
    """Actualiza un artículo."""
    if current_user.get('preferred_username'):
        article_data.last_editor = current_user.get('preferred_username')
        
    article = HelpCenterService.update(db, article_uuid, article_data)
    if not article:
        raise HTTPException(status_code=404, detail="Article not found")
    
    trigger_sync_or_broadcast(article.uuid)
    
    return article

@router.delete("/articles/{article_uuid}/", status_code=status.HTTP_204_NO_CONTENT)
def delete_article(
    article_uuid: UUID,
    db: Session = Depends(get_core_db),
    current_user: Dict[str, Any] = Depends(has_role("admin"))
):
    """Elimina un artículo."""
    if not HelpCenterService.delete(db, article_uuid):
        raise HTTPException(status_code=404, detail="Article not found")
    
    # Broadcast or Sync the deletion? 
    # Current sync logic relies on sending the *content*. Deletion sync is harder because the article is gone.
    # For now, let's at least trigger the logic.
    # WARNING: sync_single_article_task expects the article to exist to send it.
    # If we deleted it locally, sync_single_article_task will fail or send nothing.
    # We need a dedicated 'sync_deletion' task or similar. 
    # Since the user didn't explicitly ask for deletion sync, I will SKIP adding complex deletion sync 
    # logic right now to avoid breaking things, but I'll add the hook for completeness.
    # Actually, better to NOT trigger sync on delete if we don't handle it, to avoid errors in logs.
    
    return None
