from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy.orm import Session
import httpx

from app.config import settings
from app.database import get_db
from app.models.user import User
from app.models.ai_model_config import AIModelConfig
from app.schemas.auth import UserResponse, UserUpdateRole, UserCreate
from app.schemas.admin import AIModelConfigCreate, AIModelConfigResponse, AIModelConfigUpdate
from app.utils.auth import require_admin, get_current_user, get_password_hash

router = APIRouter()


# --- User Management ---

@router.get("/users", response_model=list[UserResponse])
def list_users(
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    return db.query(User).order_by(User.created_at.desc()).all()


@router.put("/users/{user_id}/role", response_model=UserResponse)
def update_user_role(
    user_id: int,
    role_data: UserUpdateRole,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    if role_data.role not in ("admin", "moderator", "user"):
        raise HTTPException(status_code=400, detail="Role must be admin, moderator, or user")

    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    if user.id == admin.id:
        raise HTTPException(status_code=400, detail="Cannot change your own role")

    user.role = role_data.role
    db.commit()
    db.refresh(user)
    return user


@router.put("/users/{user_id}/unthrottle", response_model=UserResponse)
def set_user_unthrottle(
    user_id: int,
    data: dict,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    """Set or clear the unthrottle flag for a user — exempt from AI/TTS rate limits."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    user.is_unthrottled = 1 if data.get("unthrottled") else 0
    db.commit()
    db.refresh(user)
    return user


@router.delete("/users/{user_id}", status_code=204)
def delete_user(
    user_id: int,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    """Delete a user and all their data (attempts, favorites, settings)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    if user.id == admin.id:
        raise HTTPException(status_code=400, detail="Cannot delete yourself")
    # Nullify non-cascading FKs before delete
    from sqlalchemy import text
    db.execute(text("UPDATE question_categories SET user_id = NULL WHERE user_id = :uid"), {"uid": user_id})
    db.execute(text("UPDATE quiz_categories SET user_id = NULL WHERE user_id = :uid"), {"uid": user_id})
    db.delete(user)
    db.commit()


@router.post("/users", response_model=UserResponse)
def create_user(
    user_data: UserCreate,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    """Admin creates a user directly — email is auto-verified."""
    from app.models.email_verification import EmailVerification
    from datetime import datetime

    email_normalized = user_data.email.lower().strip()
    if db.query(User).filter(User.email == email_normalized).first():
        raise HTTPException(status_code=400, detail="Email already registered")

    user = User(
        email=email_normalized,
        hashed_password=get_password_hash(user_data.password),
        name=user_data.name,
        role="user",
    )
    db.add(user)
    db.flush()
    db.add(EmailVerification(
        user_id=user.id,
        token=f"admin_created_{user.id}",
        expires_at=datetime.utcnow(),
        verified_at=datetime.utcnow(),
    ))
    db.commit()
    db.refresh(user)
    return user


# --- AI Model Configuration ---

@router.get("/models/available")
def list_available_models(
    task: str = Query("extraction"),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """Returns active models for a given task — for users to choose when taking/creating a quiz."""
    models = db.query(AIModelConfig).filter(
        AIModelConfig.task == task,
        AIModelConfig.is_active == True,
    ).order_by(AIModelConfig.is_default.desc(), AIModelConfig.name).all()
    result = [{"id": m.id, "name": m.name, "model_id": m.model_id, "is_default": m.is_default} for m in models]
    # Always include env default as fallback if nothing configured
    if not result:
        result.append({"id": None, "name": "Default (from config)", "model_id": settings.LITELLM_MODEL, "is_default": True})
    return result


class LiteLLMSearchRequest(BaseModel):
    api_key: str | None = None
    api_base: str | None = None


@router.post("/litellm/models")
def search_litellm_models(
    data: LiteLLMSearchRequest,
    admin: User = Depends(require_admin),
):
    """Query available models from LiteLLM proxy or OpenAI-compatible API."""
    import logging
    log = logging.getLogger(__name__)
    base = (data.api_base or settings.LITELLM_API_BASE or "").rstrip("/")
    key = data.api_key or settings.LITELLM_API_KEY

    if base:
        try:
            headers = {"Authorization": f"Bearer {key}"} if key else {}
            resp = httpx.post(f"{base}/v1/models", headers=headers, timeout=10) if False else \
                   httpx.get(f"{base}/v1/models", headers=headers, timeout=10)
            resp.raise_for_status()
            models = sorted([m["id"] for m in resp.json().get("data", [])])
            return {"models": models, "source": base}
        except Exception as e:
            log.warning(f"LiteLLM model search failed: {e}")
            raise HTTPException(status_code=400, detail=f"Failed to query models API: {e}")

    try:
        import litellm
        models = sorted(litellm.utils.get_valid_models())
        return {"models": models, "source": "litellm-builtin"}
    except Exception as e:
        log.warning(f"LiteLLM builtin model list failed: {e}")
        raise HTTPException(status_code=500, detail="Failed to retrieve LiteLLM built-in model list.")


@router.get("/models", response_model=list[AIModelConfigResponse])
def list_models(
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    return db.query(AIModelConfig).order_by(AIModelConfig.task, AIModelConfig.name).all()


@router.post("/models", response_model=AIModelConfigResponse)
def create_model(
    data: AIModelConfigCreate,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    if data.task not in ("extraction", "tts", "teach", "keyword", "flashcard"):
        raise HTTPException(status_code=400, detail="Task must be extraction, tts, teach, or keyword")

    if data.is_default:
        db.query(AIModelConfig).filter(
            AIModelConfig.task == data.task,
            AIModelConfig.is_default == True,
        ).update({"is_default": False})

    # Auto-set as default if this is the first model for the task
    existing = db.query(AIModelConfig).filter(
        AIModelConfig.task == data.task, AIModelConfig.is_active == True,
    ).count()
    if existing == 0:
        data_dict = data.model_dump()
        data_dict["is_default"] = True
    else:
        data_dict = data.model_dump()

    model = AIModelConfig(**data_dict)
    db.add(model)
    try:
        db.commit()
    except Exception as e:
        db.rollback()
        if "uq_model_task" in str(e).lower() or "unique" in str(e).lower():
            raise HTTPException(status_code=409, detail=f"Model '{data.model_id}' already exists for task '{data.task}'")
        raise HTTPException(status_code=500, detail=str(e))
    db.refresh(model)
    return model


@router.put("/models/{model_id}", response_model=AIModelConfigResponse)
def update_model(
    model_id: int,
    data: AIModelConfigUpdate,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    model = db.query(AIModelConfig).filter(AIModelConfig.id == model_id).first()
    if not model:
        raise HTTPException(status_code=404, detail="Model config not found")

    update_data = data.model_dump(exclude_unset=True)

    task = update_data.get("task", model.task)
    if update_data.get("is_default"):
        db.query(AIModelConfig).filter(
            AIModelConfig.task == task,
            AIModelConfig.is_default == True,
            AIModelConfig.id != model_id,
        ).update({"is_default": False})

    for key, value in update_data.items():
        setattr(model, key, value)

    db.commit()
    db.refresh(model)
    return model


@router.delete("/models/{model_id}", status_code=204)
def delete_model(
    model_id: int,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    model = db.query(AIModelConfig).filter(AIModelConfig.id == model_id).first()
    if not model:
        raise HTTPException(status_code=404, detail="Model config not found")
    db.delete(model)
    db.commit()


@router.post("/models/{model_id}/test")
def test_model(
    model_id: int,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin),
):
    """Send a simple test completion to verify an LLM model is reachable.
    TTS models are previewed via /tts/speak instead."""
    model = db.query(AIModelConfig).filter(AIModelConfig.id == model_id).first()
    if not model:
        raise HTTPException(status_code=404, detail="Model config not found")

    if model.task == "tts":
        raise HTTPException(status_code=400, detail="Use the Preview button to test TTS voices — it plays audio directly.")

    try:
        import litellm
        from app.services.ai_service import _proxy_model
        use_model = _proxy_model(model.model_id)
        kwargs = {
            "model": use_model,
            "messages": [{"role": "user", "content": "Reply with only the word: OK"}],
            "max_tokens": 10,
        }
        if model.api_key:
            kwargs["api_key"] = model.api_key
        elif settings.LITELLM_API_KEY:
            kwargs["api_key"] = settings.LITELLM_API_KEY
        if settings.LITELLM_API_BASE:
            kwargs["api_base"] = settings.LITELLM_API_BASE
        response = litellm.completion(**kwargs)
        reply = response.choices[0].message.content.strip()
        return {"message": f"✓ {model.model_id} → {reply!r}"}
    except Exception as e:
        raise HTTPException(status_code=502, detail=str(e))


class TTSVoiceSearchRequest(BaseModel):
    provider: str
    api_key: str | None = None
    region: str | None = None


@router.post("/tts/voices")
def search_tts_voices(
    data: TTSVoiceSearchRequest,
    admin: User = Depends(require_admin),
):
    """Discover available TTS voices from ElevenLabs, AWS Polly, or return OpenAI hardcoded list."""
    import logging
    log = logging.getLogger(__name__)
    provider = data.provider
    api_key = data.api_key
    region = data.region

    if provider == "elevenlabs":
        key = api_key or settings.ELEVENLABS_API_KEY
        if not key:
            raise HTTPException(status_code=400, detail="ElevenLabs API key required (set ELEVENLABS_API_KEY in .env or enter it above)")
        try:
            resp = httpx.get(
                "https://api.elevenlabs.io/v1/voices",
                headers={"xi-api-key": key},
                timeout=15,
            )
            resp.raise_for_status()
            voices = resp.json().get("voices", [])
            return {"voices": [
                {
                    "model_id": f"elevenlabs/{v['voice_id']}",
                    "name": v["name"],
                    "labels": v.get("labels", {}),
                }
                for v in sorted(voices, key=lambda x: x["name"])
            ]}
        except HTTPException:
            raise
        except Exception as e:
            log.warning(f"ElevenLabs voice discovery failed: {e}")
            raise HTTPException(status_code=400, detail=f"ElevenLabs API error: {e}")

    elif provider == "polly":
        access_key = api_key or settings.AWS_ACCESS_KEY_ID
        secret_key = settings.AWS_SECRET_ACCESS_KEY
        aws_region = region or settings.AWS_REGION or "us-east-1"
        if not access_key or not secret_key:
            raise HTTPException(
                status_code=400,
                detail="AWS credentials required — set AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY in .env",
            )
        try:
            import boto3
            polly = boto3.client(
                "polly",
                aws_access_key_id=access_key,
                aws_secret_access_key=secret_key,
                region_name=aws_region,
            )
            resp = polly.describe_voices(Engine="neural")
            voices = resp.get("Voices", [])
            return {"voices": [
                {
                    "model_id": f"polly/{v['Id']}",
                    "name": f"{v['Name']} — {v['LanguageName']} ({v.get('Gender', '')})",
                    "labels": {"gender": v.get("Gender", ""), "language": v.get("LanguageName", "")},
                }
                for v in sorted(voices, key=lambda x: x["Name"])
            ]}
        except HTTPException:
            raise
        except Exception as e:
            log.warning(f"AWS Polly voice discovery failed: {e}")
            raise HTTPException(status_code=400, detail=f"AWS Polly error: {e}")

    elif provider == "openai":
        voices = []
        for model_name in ["tts-1", "tts-1-hd"]:
            for voice in ["alloy", "ash", "coral", "echo", "fable", "nova", "onyx", "sage", "shimmer"]:
                voices.append({
                    "model_id": f"{model_name}:{voice}",
                    "name": f"{model_name} · {voice}",
                    "labels": {"model": model_name, "voice": voice},
                })
        return {"voices": voices}

    else:
        raise HTTPException(status_code=400, detail=f"Unknown provider '{provider}'. Valid: elevenlabs, polly, openai")


# --- System Settings ---

@router.get("/settings")
def get_settings(admin: User = Depends(require_admin)):
    """Get system settings."""
    try:
        import redis as redis_lib
        r = redis_lib.from_url(settings.REDIS_URL, decode_responses=True)
        registration_enabled = r.get("settings:registration_enabled")
        embedding_model = r.get("settings:embedding_model")
        polly_enabled = r.get("settings:polly_enabled")
        sso_only = r.get("settings:sso_only")
        return {
            "registration_enabled": registration_enabled != "false",
            "embedding_model": embedding_model or settings.LITELLM_EMBEDDING_MODEL or "",
            "polly_enabled": polly_enabled != "false",
            "sso_only": sso_only == "true",
            "sso_configured": bool(settings.OIDC_PROVIDER_URL and settings.OIDC_CLIENT_ID),
            "sso_provider_name": settings.OIDC_PROVIDER_NAME,
        }
    except Exception:
        return {
            "registration_enabled": True,
            "embedding_model": settings.LITELLM_EMBEDDING_MODEL or "",
            "polly_enabled": True,
            "sso_only": False,
            "sso_configured": bool(settings.OIDC_PROVIDER_URL and settings.OIDC_CLIENT_ID),
            "sso_provider_name": settings.OIDC_PROVIDER_NAME,
        }


@router.put("/settings")
def update_settings(
    settings_data: dict,
    admin: User = Depends(require_admin),
):
    """Update system settings."""
    try:
        import redis as redis_lib
        r = redis_lib.from_url(settings.REDIS_URL, decode_responses=True)

        if "registration_enabled" in settings_data:
            value = "true" if settings_data["registration_enabled"] else "false"
            r.set("settings:registration_enabled", value)

        if "embedding_model" in settings_data:
            r.set("settings:embedding_model", settings_data["embedding_model"])

        if "polly_enabled" in settings_data:
            value = "true" if settings_data["polly_enabled"] else "false"
            r.set("settings:polly_enabled", value)

        if "sso_only" in settings_data:
            value = "true" if settings_data["sso_only"] else "false"
            r.set("settings:sso_only", value)

        return {"success": True, "message": "Settings updated"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Failed to update settings: {str(e)}")


@router.post("/embedding/test")
def test_embedding(admin: User = Depends(require_admin)):
    """Test the currently configured embedding model."""
    from app.services.embedding_service import generate_embedding, _get_embedding_model
    model = _get_embedding_model()
    if not model:
        raise HTTPException(status_code=400, detail="No embedding model configured")
    result = generate_embedding("The quick brown fox jumps over the lazy dog")
    if result is None:
        raise HTTPException(status_code=500, detail=f"Embedding failed for model: {model}")
    return {"model": model, "dimensions": len(result), "status": "ok"}


@router.post("/embedding/regenerate")
def regenerate_embeddings(admin: User = Depends(require_admin)):
    """Queue a background Celery task to regenerate all question embeddings."""
    import uuid
    from app.tasks.quiz_tasks import regenerate_embeddings as regen_task
    job_id = str(uuid.uuid4())
    regen_task.delay(job_id, admin.id)
    return {"job_id": job_id, "message": "Regeneration started — progress visible in the Jobs badge."}
