import json
import logging
import re

from openai import OpenAI

from rhodecode.apps.ai_agents.ai_settings import AISettings
from rhodecode.apps.ai_agents.models.base import AIService, PingRequest, CodeReviewRequest, CustomFunctions


class GPTService(AIService):
    def __init__(self, model_settings: AISettings):
        super().__init__(model_settings)
        self._client = OpenAI(api_key=self.model_settings.api_key)
        self.log = logging.getLogger(GPTService.__name__)

    def list_model_api_and_display_names(self) -> list[tuple[str, str]]:
        models = self.list_models()
        res = []
        for m in models:
            if getattr(m, "id"):
                model_api_name = m.id
                res.append((model_api_name, self._to_display_name(model_api_name)))
            else:
                self.log.warning(f"Model {m} has no id")
        return self._filter_api_models(res)

    def _list_non_text_models(self) -> list[str]:
        return [
            "sora",
            "audio",
            "dall-e",
            "realtime",
            "image",
            "video",
            "codex",
            "search",
            "babbage",
            "moderation",
            "davinci",
            "transcribe",
            "tts",
            "whisper",
            "embedding",
        ]

    def _to_display_name(self, api_model_name: str) -> str:
        s = api_model_name
        sentinel = "\x00"

        # Protect the "dall-e" brand hyphen so it stays as "dall-e"
        s = re.sub(r"\bdall-e\b", lambda m: m.group(0).replace("-", sentinel), s, flags=re.IGNORECASE)

        # Protect dates (YYYY-MM-DD and MM-DD)
        def _protect_date(m):
            return m.group(0).replace("-", sentinel)

        s = re.sub(r"\b\d{4}-\d{2}-\d{2}\b", _protect_date, s)  # YYYY-MM-DD
        s = re.sub(r"\b\d{2}-\d{2}\b", _protect_date, s)  # MM-DD

        # Protect version-like hyphens after 2+ letters when the next token looks like a version:
        # - 1 digit optionally followed by .digits and/or letters (e.g., 4, 3.5, 4o)
        # - OR exactly 2 digits (e.g., 13) but not 3+ digits (avoid years like 2025)
        s = re.sub(
            r"([A-Za-z]{2,})-(?=(?:\d(?:\.\d+)?[A-Za-z]*|\d{2}(?!\d))(?:$|[-\s]))", lambda m: m.group(1) + sentinel, s
        )

        # Replace all remaining hyphens with spaces
        s = s.replace("-", " ")

        # Restore protected hyphens
        return s.replace(sentinel, "-")

    def _get_response(self, request: PingRequest | CodeReviewRequest):
        _input = self._get_response_input(request)

        return self._client.responses.create(
            model=self.get_api_model_name(),
            input=_input,
            tools=[
                self._get_function(request.function_name),
            ],
            tool_choice={"type": "function", "name": request.function_name},
            parallel_tool_calls=False,
        )

    def _transform(self, resp):
        for item in resp.output:
            if item.type == "function_call" and item.name in [CustomFunctions.Code_review, CustomFunctions.Ping]:
                return super()._transform(
                    resp=json.loads(item.arguments),
                    model=resp.model,
                )

        raise ValueError("No function call found in response")
