from __future__ import annotations

from openai import APIConnectionError, AuthenticationError, OpenAI, OpenAIError

from cli_demo.config import (
    get_effective_api_key,
    get_effective_base_url,
    get_effective_model,
    load_config,
    normalize_optional_value,
)


class AIServiceError(RuntimeError):
    pass


class EmptyPromptError(AIServiceError):
    pass


class MissingAPIKeyError(AIServiceError):
    pass


class EmptyResponseError(AIServiceError):
    pass


class OpenAIRequestError(AIServiceError):
    pass


def ask_model(*, prompt: str, model: str | None, system_prompt: str | None = None) -> str:
    normalized_prompt = _normalize_required(prompt, field_name="prompt")
    normalized_system = normalize_optional_value(system_prompt, field_name="system")

    config = load_config()
    normalized_model = get_effective_model(config, override=model)
    api_key = get_effective_api_key(config)
    base_url = get_effective_base_url(config)

    if not api_key:
        raise MissingAPIKeyError(
            'API key is not configured. Run "cli-demo config set --api-key <your-api-key>" or set OPENAI_API_KEY.'
        )

    client_kwargs: dict[str, str] = {"api_key": api_key}
    if base_url:
        client_kwargs["base_url"] = base_url

    client = OpenAI(**client_kwargs)
    input_items: list[dict[str, object]] = []
    if normalized_system:
        input_items.append({"role": "system", "content": normalized_system})
    input_items.append({"role": "user", "content": normalized_prompt})

    try:
        response = client.responses.create(
            model=normalized_model,
            input=input_items,
        )
    except AuthenticationError as exc:
        raise OpenAIRequestError(
            "OpenAI authentication failed. Check your configured api_key and try again."
        ) from exc
    except APIConnectionError as exc:
        raise OpenAIRequestError(
            "Could not reach the OpenAI API. Check your network connection or configured base_url."
        ) from exc
    except OpenAIError as exc:
        raise OpenAIRequestError(f"OpenAI API error: {exc}") from exc

    output_text = response.output_text.strip() if response.output_text else ""
    if not output_text:
        raise EmptyResponseError("The model returned no printable text.")

    return output_text


def _normalize_required(value: str, *, field_name: str) -> str:
    normalized = normalize_optional_value(value, field_name=field_name)
    if normalized is None:
        raise EmptyPromptError(f"{field_name} cannot be empty or whitespace.")
    return normalized
