"""
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>

AISBF - AI Service Broker Framework || AI Should Be Free

"shodan" provider handler — a credential-free, last-resort failover provider.

It routes OpenAI-compatible requests to a pool of open model endpoints that are
discovered and validated by the vendored open-router engine (see
open_router_engine.py, by Pasquale Minervini / @pminervini). It is intended to
be added to a rotation as a ``last_resort`` entry: the rotation engages it only
when every normal provider is disabled or rate limited.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""
import logging
from typing import Dict, List, Optional, Union

from ..base import BaseProviderHandler
from ...config import config
from ...models import Model
from .runtime import get_runtime

logger = logging.getLogger(__name__)


class ShodanProviderHandler(BaseProviderHandler):
    """Last-resort provider backed by auto-discovered open model endpoints.

    Credential-free by design: the underlying engine deliberately never forwards
    client credentials to the (untrusted) discovered upstreams, and the provider
    itself needs no API key.
    """

    def __init__(
        self,
        provider_id: str,
        api_key: Optional[str] = None,
        user_id: Optional[int] = None,
        provider_config=None,
    ):
        self.provider_config = (
            provider_config if provider_config is not None else config.providers.get(provider_id)
        )
        super().__init__(provider_id, api_key, user_id=user_id)

    def _shodan_config(self) -> Dict:
        pc = self.provider_config
        if pc is None:
            return {}
        if isinstance(pc, dict):
            return pc.get("shodan_config") or {}
        return getattr(pc, "shodan_config", None) or {}

    def validate_credentials(self) -> bool:
        # No credentials are used or required — the provider is always "valid".
        return True

    async def handle_request(
        self,
        model: str,
        messages: List[Dict],
        max_tokens: Optional[int] = None,
        temperature: Optional[float] = 1.0,
        stream: Optional[bool] = False,
        tools: Optional[List[Dict]] = None,
        tool_choice: Optional[Union[str, Dict]] = None,
    ) -> Union[Dict, object]:
        if self.is_rate_limited():
            raise Exception("Provider rate limited")

        await self.apply_rate_limit()

        body: Dict = {
            "model": model,
            "messages": messages,
            "stream": bool(stream),
        }
        if max_tokens is not None:
            body["max_tokens"] = max_tokens
        if temperature is not None:
            body["temperature"] = temperature
        if tools is not None:
            body["tools"] = tools
        if tool_choice is not None:
            body["tool_choice"] = tool_choice

        try:
            runtime = await get_runtime(self._shodan_config())
            result = await runtime.route("chat/completions", body, bool(stream))
        except Exception as e:
            logger.error("ShodanProviderHandler: request failed: %s", e)
            self.record_failure()
            raise

        # Streaming returns a lazy async generator; the rotation primes and
        # consumes it and records success itself (mirrors the OpenAI handler).
        if not stream:
            self.record_success()
        return result

    async def get_models(self) -> List[Model]:
        try:
            runtime = await get_runtime(self._shodan_config())
            model_ids = await runtime.available_models()
        except Exception as e:
            logger.error("ShodanProviderHandler: get_models failed: %s", e)
            raise
        return [
            Model(id=mid, name=mid, provider_id=self.provider_id)
            for mid in model_ids
        ]
