
import httpx
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from common_logging import get_logger
from sqlalchemy.orm import Session

from app.api.deps import get_db
from app.api.permissions import require_create, require_delete, require_read, require_update
from app.core.i18n import get_translator
from app.models import Model, ModelProvider, User
from app.schemas import (
    ModelCreate,
    ModelResponse,
    ModelUpdate,
    ProviderConfig,
    ProviderCreate,
    ProviderResponse,
    ProviderUpdate,
)

logger = get_logger(__name__)
router = APIRouter(tags=["providers"])


def _provider_to_response(provider: ModelProvider) -> dict:
    return {
        "id": provider.id,
        "name": provider.name,
        "description": provider.description,
        "icon": provider.icon,
        "color": provider.color,
        "default_base_url": provider.default_base_url,
        "configured": provider.configured,
        "api_key": provider.api_key,
        "base_url": provider.base_url,
        "priority": provider.priority,
        "enabled": provider.enabled,
        "provider_kind": provider.provider_kind,
        "protocol": provider.protocol,
        "auth_type": provider.auth_type,
        "capabilities": provider.capabilities or [],
        "is_local": provider.is_local,
        "healthcheck_path": provider.healthcheck_path,
        "extra_config": provider.extra_config,
        "created_at": provider.created_at,
        "updated_at": provider.updated_at,
        "models": [_model_to_response(model) for model in provider.models or []],
    }


def _model_to_response(model: Model) -> dict:
    return {
        "id": model.id,
        "code": model.code,
        "name": model.name,
        "type": model.type,
        "tags": model.tags or [],
        "enabled": model.enabled,
        "dimension": model.dimension,
        "remote_model_id": model.remote_model_id,
        "context_length": model.context_length,
        "max_output_tokens": model.max_output_tokens,
        "supports_stream": model.supports_stream,
        "supports_tools": model.supports_tools,
        "priority": model.priority,
        "extra_config": model.extra_config,
        "provider_id": model.provider_id,
        "provider_name": model.provider.name if model.provider else None,
        "is_local": model.provider.is_local if model.provider else False,
        "created_at": model.created_at,
        "updated_at": model.updated_at,
    }


@router.get("/models/available", response_model=list[ModelResponse])
def get_available_models(
    type: str | None = Query(
        None, description="Filter by model type (e.g., 'embedding', 'chat')"
    ),
    db: Session = Depends(get_db),
    current_user: User = Depends(require_read("models")),
):
    query = (
        db.query(Model)
        .join(ModelProvider)
        .filter(
            Model.enabled, ModelProvider.configured, ModelProvider.enabled
        )
    )
    if type:
        query = query.filter(Model.type == type)
    models = query.order_by(Model.priority.asc(), Model.id.asc()).all()
    return [_model_to_response(model) for model in models]


@router.get("/", response_model=list[ProviderResponse])
def get_providers(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_read("providers")),
    capability: str | None = Query(
        None, description="Filter by capability: chat, embedding, rerank"
    ),
):
    query = db.query(ModelProvider)
    if capability:
        query = query.filter(ModelProvider.capabilities.contains([capability]))
    providers = query.order_by(ModelProvider.priority.asc(), ModelProvider.id.asc()).all()
    return [_provider_to_response(provider) for provider in providers]


@router.get("/{provider_id}", response_model=ProviderResponse)
def get_provider(
    provider_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_read("providers")),
):
    t = get_translator(request)
    provider = db.query(ModelProvider).filter(ModelProvider.id == provider_id).first()
    if not provider:
        raise HTTPException(status_code=404, detail=t.t("provider.not_found"))
    return _provider_to_response(provider)


@router.post("/", response_model=ProviderResponse, status_code=status.HTTP_201_CREATED)
def create_provider(
    provider: ProviderCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_create("providers")),
):
    logger.bind(provider_name=provider.name, user_id=current_user.id).info("Creating provider")
    payload = provider.model_dump()
    api_key = payload.pop("api_key", None)
    db_provider = ModelProvider(**payload)
    if api_key:
        db_provider.set_api_key(api_key)
        db_provider.configured = True
    db.add(db_provider)
    db.commit()
    db.refresh(db_provider)
    logger.bind(provider_id=db_provider.id).info("Provider created")
    return _provider_to_response(db_provider)


@router.put("/{provider_id}", response_model=ProviderResponse)
def update_provider(
    provider_id: int,
    provider: ProviderUpdate,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_update("providers")),
):
    t = get_translator(request)
    logger.bind(provider_id=provider_id, user_id=current_user.id).info("Updating provider")
    db_provider = db.query(ModelProvider).filter(ModelProvider.id == provider_id).first()
    if not db_provider:
        logger.bind(provider_id=provider_id).warning("Provider not found for update")
        raise HTTPException(status_code=404, detail=t.t("provider.not_found"))
    for key, value in provider.model_dump(exclude_unset=True).items():
        if key == "api_key":
            db_provider.set_api_key(value)
            db_provider.configured = bool(value or db_provider.base_url or db_provider.is_local)
            continue
        setattr(db_provider, key, value)
    db.commit()
    db.refresh(db_provider)
    logger.bind(provider_id=provider_id).info("Provider updated")
    return _provider_to_response(db_provider)


@router.post("/{provider_id}/configure", response_model=ProviderResponse)
def configure_provider(
    provider_id: int,
    config: ProviderConfig,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_update("providers")),
):
    t = get_translator(request)
    logger.bind(provider_id=provider_id, user_id=current_user.id).info("Configuring provider")
    db_provider = db.query(ModelProvider).filter(ModelProvider.id == provider_id).first()
    if not db_provider:
        logger.bind(provider_id=provider_id).warning("Provider not found for configuration")
        raise HTTPException(status_code=404, detail=t.t("provider.not_found"))
    if config.api_key:
        db_provider.set_api_key(config.api_key)
    db_provider.base_url = config.base_url or db_provider.default_base_url
    db_provider.priority = config.priority
    db_provider.enabled = config.enabled
    db_provider.configured = bool(
        db_provider.base_url or db_provider.is_local or db_provider.api_key
    )
    db.commit()
    db.refresh(db_provider)
    logger.bind(provider_id=provider_id).info("Provider configured")
    return _provider_to_response(db_provider)


@router.delete("/{provider_id}", status_code=status.HTTP_200_OK)
def delete_provider(
    provider_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_delete("providers")),
):
    t = get_translator(request)
    logger.bind(provider_id=provider_id, user_id=current_user.id).info("Deleting provider")
    db_provider = db.query(ModelProvider).filter(ModelProvider.id == provider_id).first()
    if not db_provider:
        logger.bind(provider_id=provider_id).warning("Provider not found for deletion")
        raise HTTPException(status_code=404, detail=t.t("provider.not_found"))
    db.delete(db_provider)
    db.commit()
    logger.bind(provider_id=provider_id).info("Provider deleted")
    return {"success": True, "message": t.t("provider.deleted")}


@router.get("/{provider_id}/models", response_model=list[ModelResponse])
def get_provider_models(
    provider_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_read("models")),
):
    models = db.query(Model).filter(Model.provider_id == provider_id).all()
    return [_model_to_response(model) for model in models]


@router.post(
    "/{provider_id}/models", response_model=ModelResponse, status_code=status.HTTP_201_CREATED
)
def add_model(
    provider_id: int,
    model: ModelCreate,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_create("models")),
):
    t = get_translator(request)
    logger.bind(provider_id=provider_id, model_code=model.code, user_id=current_user.id).info("Adding model to provider")
    provider = db.query(ModelProvider).filter(ModelProvider.id == provider_id).first()
    if not provider:
        logger.bind(provider_id=provider_id).warning("Provider not found for model addition")
        raise HTTPException(status_code=404, detail=t.t("provider.not_found"))
    db_model = Model(**model.model_dump())
    db.add(db_model)
    db.commit()
    db.refresh(db_model)
    logger.bind(provider_id=provider_id, model_id=db_model.id).info("Model added to provider")
    return _model_to_response(db_model)


@router.put("/models/{model_id}", response_model=ModelResponse)
def update_model(
    model_id: int,
    model: ModelUpdate,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_update("models")),
):
    t = get_translator(request)
    db_model = db.query(Model).filter(Model.id == model_id).first()
    if not db_model:
        raise HTTPException(status_code=404, detail=t.t("model.not_found"))
    for key, value in model.model_dump(exclude_unset=True).items():
        setattr(db_model, key, value)
    db.commit()
    db.refresh(db_model)
    return _model_to_response(db_model)


@router.post("/{provider_id}/health-check", status_code=status.HTTP_200_OK)
async def health_check_provider(
    provider_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_update("providers")),
):
    t = get_translator(request)
    provider = db.query(ModelProvider).filter(ModelProvider.id == provider_id).first()
    if not provider:
        raise HTTPException(status_code=404, detail=t.t("provider.not_found"))
    base_url = provider.base_url or provider.default_base_url
    if not base_url:
        raise HTTPException(status_code=400, detail="Provider base URL is required")
    path = provider.healthcheck_path or "/health"
    url = f"{base_url.rstrip('/')}{(path if path.startswith('/') else '/' + path)}"
    headers = {}
    api_key = provider.get_api_key()
    if provider.auth_type == "bearer" and api_key:
        headers["Authorization"] = f"Bearer {api_key}"
    elif provider.auth_type == "api_key" and api_key:
        headers["api-key"] = api_key
    elif provider.auth_type == "x_api_key" and api_key:
        headers["x-api-key"] = api_key
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(url, headers=headers)
        logger.info(f"Provider health check: provider_id={provider_id}, status={response.status_code}")
        return {"success": response.is_success, "status_code": response.status_code, "url": url}
    except Exception as exc:
        logger.error(f"Provider health check failed: provider_id={provider_id}, error={exc}")
        raise HTTPException(status_code=502, detail=f"Health check failed: {exc}") from None


@router.delete("/models/{model_id}", status_code=status.HTTP_200_OK)
def delete_model(
    model_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_delete("models")),
):
    t = get_translator(request)
    db_model = db.query(Model).filter(Model.id == model_id).first()
    if not db_model:
        raise HTTPException(status_code=404, detail=t.t("model.not_found"))
    db.delete(db_model)
    db.commit()
    return {"success": True, "message": t.t("model.deleted")}


@router.post("/models/{model_id}/toggle", status_code=status.HTTP_200_OK)
def toggle_model(
    model_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_update("models")),
):
    t = get_translator(request)
    db_model = db.query(Model).filter(Model.id == model_id).first()
    if not db_model:
        raise HTTPException(status_code=404, detail=t.t("model.not_found"))
    db_model.enabled = not db_model.enabled
    db.commit()
    db.refresh(db_model)
    return {"success": True, "enabled": db_model.enabled}
