"""Provider and model configuration endpoints."""

import json
import logging
import os
from typing import Optional, List, Dict, Any

from fastapi import APIRouter
from pydantic import BaseModel, Field

from src.config.settings import REDIS_ENABLED
from src.models.schemas import ResponseModel
from src.services.cache_service import get_cache_service
from src.services.provider.registry import ModelRegistry, ModelType
from src.utils.errors import BusinessException, ErrorCode, ResourceNotFoundException, InvalidParameterException
from src.utils.service_loader import register_service

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


class ModelRegistrationRequest(BaseModel):
    """Request to register a new model."""
    name: str
    model_id: str
    provider: str
    type: str
    family: Optional[str] = None
    capabilities: Optional[Dict[str, bool]] = None
    variants: Optional[Dict[str, str]] = None
    resolutions: Optional[Any] = None
    durations: Optional[Any] = None
    voices: Optional[List[Dict[str, str]]] = None
    args: Optional[List[Any]] = None


class ProviderKeyField(BaseModel):
    """Provider key field configuration."""
    name: str
    label: str
    placeholder: str
    required: bool
    type: Optional[str] = "text"


class ProviderConfigResponse(BaseModel):
    """Provider configuration response."""
    id: str
    name: str
    description: str
    fields: List[ProviderKeyField]
    helpUrl: Optional[str] = None


# Supported providers configuration with key field definitions
def _build_provider_configs() -> List[ProviderConfigResponse]:
    """Build provider configs from ModelRegistry metadata and registered models.

    This dynamically generates provider configurations from provider.json files,
    avoiding hardcoded configurations.
    """
    configs = []

    # Get all registered providers from ModelRegistry
    providers = ModelRegistry.list_providers()
    provider_ids = {p["id"] for p in providers}

    # Build config for each provider
    for provider_id in provider_ids:
        metadata = ModelRegistry.get_provider_metadata(provider_id) or {}

        # Get fields from metadata or use defaults
        fields_data = metadata.get("fields", [])
        fields = [ProviderKeyField(**f) for f in fields_data] if fields_data else [
            ProviderKeyField(name="apiKey", label="API Key", placeholder="sk-...", required=True, type="password")
        ]

        # Build config response
        config = ProviderConfigResponse(
            id=provider_id,
            name=metadata.get("name") or provider_id.capitalize(),
            description=metadata.get("description", ""),
            fields=fields,
            helpUrl=metadata.get("helpUrl") or metadata.get("dashboard_url")
        )
        configs.append(config)

    return configs


@router.get("/config/providers", response_model=ResponseModel)
async def get_providers():
    """Get list of supported providers and their capabilities (cached for 5 minutes)."""
    cache_key = "config:providers"

    if REDIS_ENABLED:
        cache = get_cache_service()
        cached_providers = await cache.get(cache_key)
        if cached_providers is not None:
            return ResponseModel(data=cached_providers)

    providers = ModelRegistry.list_providers()
    result = {"providers": providers}

    if REDIS_ENABLED:
        cache = get_cache_service()
        await cache.set(cache_key, result, ttl=300)

    return ResponseModel(data=result)


@router.post("/config/models", response_model=ResponseModel)
async def register_new_model(request: ModelRegistrationRequest):
    """Register a new model configuration.

    Raises:
        InvalidParameterException: Invalid model configuration
        ResourceNotFoundException: Template not found
        BusinessException: Failed to register model
    """
    try:
        provider = request.provider.lower()
        model_type = request.type.lower()
        family = request.family.lower() if request.family else "default"

        module_name = ""
        class_name = ""

        # Find template service from registry
        try:
            type_enum = None
            try:
                type_enum = ModelType(model_type)
            except ValueError:
                pass

            services = ModelRegistry.find_services(
                provider=provider,
                model_type=type_enum
            )

            if not services and not type_enum:
                all_services = ModelRegistry.find_services(provider=provider)
                services = [s for s in all_services if s.get('type') == model_type]

            if not services:
                raise ResourceNotFoundException("template", f"{provider}/{model_type}")

            template = services[0]
            if family and family != "default":
                for svc in services:
                    svc_id = svc.get('id', '').lower()
                    svc_class = (svc.get('class') or svc.get('class_name', '')).lower()
                    if family in svc_id or family in svc_class:
                        template = svc
                        break

            module_name = template.get('module')
            class_name = template.get('class') or template.get('class_name')

            if not module_name or not class_name:
                raise BusinessException(
                    ErrorCode.UNKNOWN_ERROR,
                    "Template service configuration is missing module or class",
                    {"template": template}
                )

        except (ResourceNotFoundException, BusinessException):
            raise
        except Exception as e:
            logger.error(f"Error finding template for {provider}/{model_type}: {e}", exc_info=True)
            raise BusinessException(
                ErrorCode.UNKNOWN_ERROR,
                "Failed to resolve service template",
                {"provider": provider, "type": model_type, "reason": str(e)}
            )

        service_id = f"{provider}-{request.model_id.replace('.', '-').replace('/', '-')}"

        config = {
            "id": service_id,
            "module": module_name,
            "class": class_name,
            "name": request.name,
            "args": [request.model_id],
            "type": model_type,
            "provider": provider,
            "enabled": True
        }

        if request.capabilities:
            config["capabilities"] = request.capabilities
        if request.variants:
            config["variants"] = request.variants
        if request.resolutions:
            config["resolutions"] = request.resolutions
        if request.durations:
            config["durations"] = request.durations
        if request.args:
            config["args"] = request.args

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

        os.makedirs(os.path.dirname(custom_config_path), exist_ok=True)

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

        existing_configs = [c for c in existing_configs if c.get("id") != service_id]
        existing_configs.append(config)

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

        register_service(config)

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

        return ResponseModel(data={"status": "success", "model": config})

    except (ResourceNotFoundException, BusinessException):
        raise
    except Exception as e:
        logger.error(f"Failed to register model: {e}", exc_info=True)
        raise BusinessException(
            ErrorCode.UNKNOWN_ERROR,
            "Failed to register model",
            {"reason": str(e)}
        )


@router.get("/config/models", response_model=ResponseModel)
async def get_models_config():
    """Get all registered models configuration grouped by type (cached for 5 minutes)."""
    cache_key = "config:models"

    if REDIS_ENABLED:
        cache = get_cache_service()
        cached_models = await cache.get(cache_key)
        if cached_models is not None:
            return ResponseModel(data=cached_models)

    models = ModelRegistry.list_models()

    grouped = {
        "image": {},
        "video": {},
        "audio": {},
        "lyrics": {},
        "music": {},
        "llm": {}
    }

    for model_id, config_dict in models.items():
        model_config = config_dict.copy() if isinstance(config_dict, dict) else {}

        model_type_str = model_config.get("type")
        if not model_type_str or model_type_str not in grouped:
            continue

        try:
            model_type = ModelType(model_type_str.lower())
            default_id = ModelRegistry.get_default_id(model_type)
            model_config["is_default"] = (model_id == default_id)
        except (ValueError, KeyError):
            model_config["is_default"] = False

        model_config["id"] = model_id

        if "provider" not in model_config:
            if "/" in model_id:
                model_config["provider"] = model_id.split("/", 1)[0]

        if "model_key" not in model_config:
            if "/" in model_id:
                model_config["model_key"] = model_id.split("/", 1)[1]

        grouped[model_type_str][model_id] = model_config

    if REDIS_ENABLED:
        cache = get_cache_service()
        await cache.set(cache_key, grouped, ttl=300)

    return ResponseModel(data=grouped)


@router.get("/config/models/search", response_model=ResponseModel)
async def search_models(
    provider: Optional[str] = None,
    type: Optional[str] = None,
    enabled_only: bool = True
):
    """Search for models by criteria."""
    try:
        model_type = None
        if type:
            try:
                model_type = ModelType(type.lower())
            except ValueError:
                raise InvalidParameterException("type", f"Invalid model type: {type}")

        results = ModelRegistry.find_services(
            provider=provider,
            model_type=model_type,
            enabled_only=enabled_only
        )

        return ResponseModel(data={
            "count": len(results),
            "models": results
        })
    except InvalidParameterException:
        raise
    except Exception as e:
        logger.error(f"Failed to search models: {e}", exc_info=True)
        raise BusinessException(
            ErrorCode.UNKNOWN_ERROR,
            "Failed to search models",
            {"provider": provider, "type": type, "reason": str(e)}
        )


@router.get("/config/models/{model_id}", response_model=ResponseModel)
async def get_model_config(model_id: str):
    """Get detailed configuration for a specific model."""
    try:
        config = ModelRegistry.get_config(model_id)
        if not config:
            raise ResourceNotFoundException("model", model_id)

        return ResponseModel(data=config)
    except ResourceNotFoundException:
        raise
    except Exception as e:
        logger.error(f"Failed to get model config: {e}", exc_info=True)
        raise BusinessException(
            ErrorCode.UNKNOWN_ERROR,
            "Failed to get model configuration",
            {"model_id": model_id, "reason": str(e)}
        )


@router.get("/config/defaults", response_model=ResponseModel)
async def get_defaults():
    """Get default models for each type (cached for 5 minutes)."""
    cache_key = "config:defaults"

    if REDIS_ENABLED:
        cache = get_cache_service()
        cached_defaults = await cache.get(cache_key)
        if cached_defaults is not None:
            return ResponseModel(data=cached_defaults)

    try:
        defaults = {}
        for model_type in ModelType:
            default_id = ModelRegistry.get_default_id(model_type)
            if default_id:
                defaults[model_type.value] = {
                    "id": default_id,
                    "config": ModelRegistry.get_config(default_id)
                }

        if REDIS_ENABLED:
            cache = get_cache_service()
            await cache.set(cache_key, defaults, ttl=300)

        return ResponseModel(data=defaults)
    except Exception as e:
        logger.error(f"Failed to get defaults: {e}", exc_info=True)
        raise BusinessException(
            ErrorCode.UNKNOWN_ERROR,
            "Failed to get default models",
            {"reason": str(e)}
        )


@router.get("/config/provider-configs", response_model=ResponseModel)
async def get_provider_configs():
    """Get supported provider configurations for user API key management.

    Configurations are dynamically built from provider.json files,
    ensuring a single source of truth for provider metadata.
    """
    cache_key = "config:provider_configs"

    if REDIS_ENABLED:
        cache = get_cache_service()
        cached_configs = await cache.get(cache_key)
        if cached_configs is not None:
            return ResponseModel(data={"providers": cached_configs})

    # Dynamically build provider configs from registry metadata
    configs = _build_provider_configs()
    providers_data = [config.model_dump() for config in configs]

    if REDIS_ENABLED:
        cache = get_cache_service()
        await cache.set(cache_key, providers_data, ttl=300)

    return ResponseModel(data={"providers": providers_data})
