"""Provider and model management endpoints (Admin Only)."""

import json
import logging
import os

from fastapi import APIRouter, Depends

from src.config.settings import REDIS_ENABLED
from src.models.schemas import ResponseModel
from src.auth.dependencies import require_admin
from src.auth.models import UserAuth
from src.services.cache_service import get_cache_service
from src.services.provider.registry import ModelRegistry, ModelType
from src.utils.errors import AppException, ErrorCode
from src.services.provider.health import health_monitor
from src.utils.service_loader import register_service

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


@router.post("/admin/providers/{provider_id}/toggle", response_model=ResponseModel)
async def toggle_provider(
    provider_id: str,
    enabled: bool,
    current_user: UserAuth = Depends(require_admin),
):
    """Enable/disable Provider (Admin only)."""
    try:
        provider_config = ModelRegistry.get_provider_config(provider_id)
        if not provider_config:
            raise AppException(
                message=f"Provider not found: {provider_id}",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        provider_config["enabled"] = enabled

        src_dir = os.path.dirname(os.path.dirname(__file__))
        custom_providers_path = os.path.join(src_dir, "config", "services", "custom_providers.json")

        existing_configs = []
        if os.path.exists(custom_providers_path):
            try:
                with open(custom_providers_path, 'r', encoding='utf-8') as f:
                    existing_configs = json.load(f)
            except (json.JSONDecodeError, OSError) as e:
                logger.warning("Failed to load custom providers config %s: %s", custom_providers_path, e)

        updated = False
        for i, config in enumerate(existing_configs):
            if config.get("id") == provider_id:
                existing_configs[i] = {**config, **provider_config}
                updated = True
                break

        if not updated:
            existing_configs.append(provider_config)

        os.makedirs(os.path.dirname(custom_providers_path), exist_ok=True)
        with open(custom_providers_path, 'w', encoding='utf-8') as f:
            json.dump(existing_configs, f, indent=4, ensure_ascii=False)

        if enabled:
            register_service(provider_config)

        return ResponseModel(
            data={
                "provider_id": provider_id,
                "enabled": enabled,
                "message": f"Provider {'enabled' if enabled else 'disabled'} successfully",
            }
        )
    except AppException:
        raise
    except Exception as e:
        logger.error(f"Error toggling provider: {e}")
        raise AppException(
            message=str(e),
            code=ErrorCode.INTERNAL_ERROR,
            status_code=500
        )


@router.delete("/admin/providers/{provider_id}", response_model=ResponseModel)
async def delete_provider(
    provider_id: str,
    current_user: UserAuth = Depends(require_admin),
):
    """Delete custom Provider (Admin only)."""
    try:
        src_dir = os.path.dirname(os.path.dirname(__file__))
        custom_providers_path = os.path.join(src_dir, "config", "services", "custom_providers.json")

        if not os.path.exists(custom_providers_path):
            raise AppException(
                message="Custom providers configuration not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        with open(custom_providers_path, 'r', encoding='utf-8') as f:
            existing_configs = json.load(f)

        initial_len = len(existing_configs)
        existing_configs = [c for c in existing_configs if c.get("id") != provider_id]

        if len(existing_configs) == initial_len:
            raise AppException(
                message="Provider not found or not a custom provider",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        with open(custom_providers_path, 'w', encoding='utf-8') as f:
            json.dump(existing_configs, f, indent=4, ensure_ascii=False)

        return ResponseModel(
            data={
                "provider_id": provider_id,
                "deleted": True,
                "message": "Provider deleted successfully",
            }
        )
    except AppException:
        raise
    except Exception as e:
        logger.error(f"Error deleting provider: {e}")
        raise AppException(
            message=str(e),
            code=ErrorCode.INTERNAL_ERROR,
            status_code=500
        )


@router.post("/admin/models/{model_id}/toggle", response_model=ResponseModel)
async def toggle_model(
    model_id: str,
    enabled: bool,
    current_user: UserAuth = Depends(require_admin),
):
    """Enable/disable model (Admin only)."""
    try:
        model_config = ModelRegistry.get_config(model_id)
        if not model_config:
            raise AppException(
                message=f"Model not found: {model_id}",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        model_config["enabled"] = enabled

        src_dir = os.path.dirname(os.path.dirname(__file__))
        custom_models_path = os.path.join(src_dir, "config", "services", "custom_models.json")

        existing_configs = []
        if os.path.exists(custom_models_path):
            try:
                with open(custom_models_path, 'r', encoding='utf-8') as f:
                    existing_configs = json.load(f)
            except (json.JSONDecodeError, OSError) as e:
                logger.warning("Failed to load custom models config %s: %s", custom_models_path, e)

        updated = False
        for i, config in enumerate(existing_configs):
            if config.get("id") == model_id:
                existing_configs[i] = {**config, "enabled": enabled}
                updated = True
                break

        if not updated:
            existing_configs.append({**model_config, "enabled": enabled})

        os.makedirs(os.path.dirname(custom_models_path), exist_ok=True)
        with open(custom_models_path, 'w', encoding='utf-8') as f:
            json.dump(existing_configs, f, indent=4, ensure_ascii=False)

        if enabled:
            register_service(model_config)

        return ResponseModel(
            data={
                "model_id": model_id,
                "enabled": enabled,
                "message": f"Model {'enabled' if enabled else 'disabled'} successfully",
            }
        )
    except AppException:
        raise
    except Exception as e:
        logger.error(f"Error toggling model: {e}")
        raise AppException(
            message=str(e),
            code=ErrorCode.INTERNAL_ERROR,
            status_code=500
        )


@router.post("/admin/models/{model_id}/default", response_model=ResponseModel)
async def set_default_model(
    model_id: str,
    current_user: UserAuth = Depends(require_admin),
):
    """Set default model (Admin only)."""
    try:
        model_config = ModelRegistry.get_config(model_id)
        if not model_config:
            raise AppException(
                message=f"Model not found: {model_id}",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        model_type_str = model_config.get("type")
        if not model_type_str:
            raise AppException(
                message="Model type not found",
                code=ErrorCode.INVALID_PARAMETER,
                status_code=400
            )

        try:
            model_type = ModelType(model_type_str.lower())
        except ValueError:
            raise AppException(
                message=f"Invalid model type: {model_type_str}",
                code=ErrorCode.INVALID_PARAMETER,
                status_code=400
            )

        ModelRegistry.set_default_by_id(model_type, model_id)

        config_key_map = {
            "image": "defaultImageModel",
            "video": "defaultVideoModel",
            "audio": "defaultAudioModel",
            "lyrics": "defaultLyricsModel",
            "music": "defaultMusicModel",
            "llm": "defaultLLMModel",
        }

        config_key = config_key_map.get(model_type_str.lower())
        if config_key:
            src_dir = os.path.dirname(os.path.dirname(__file__))
            config_path = os.path.join(src_dir, "config", "user_config.json")

            existing_data = {}
            if os.path.exists(config_path):
                with open(config_path, 'r', encoding='utf-8') as f:
                    existing_data = json.load(f)

            existing_data[config_key] = model_id

            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(existing_data, f, indent=2, ensure_ascii=False)

        if REDIS_ENABLED:
            cache = get_cache_service()
            await cache.delete("config:defaults")
            await cache.delete("config:system")

        return ResponseModel(
            data={
                "model_id": model_id,
                "model_type": model_type_str,
                "is_default": True,
                "message": f"Default {model_type_str} model set to {model_id}",
            }
        )
    except AppException:
        raise
    except Exception as e:
        logger.error(f"Error setting default model: {e}")
        raise AppException(
            message=str(e),
            code=ErrorCode.INTERNAL_ERROR,
            status_code=500
        )


@router.post("/admin/models/{model_id}/test", response_model=ResponseModel)
async def test_model(
    model_id: str,
    current_user: UserAuth = Depends(require_admin),
):
    """Test model connection (Admin only)."""
    try:
        service = ModelRegistry.get(model_id)
        if not service:
            raise AppException(
                message=f"Model service not found: {model_id}",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        result = await health_monitor.check_service_health(model_id, service)

        health_monitor.update_health(model_id, result)

        return ResponseModel(
            data={
                "model_id": model_id,
                "success": result.status.value == "healthy",
                "status": result.status.value,
                "latency_ms": result.latency_ms,
                "message": "Model test completed",
                "error": result.error,
            }
        )
    except AppException:
        raise
    except Exception as e:
        logger.error(f"Error testing model: {e}")
        raise AppException(
            message=str(e),
            code=ErrorCode.INTERNAL_ERROR,
            status_code=500
        )
