from __future__ import annotations

import logging
from typing import Iterable, List, Tuple

from django.conf import settings
from django.utils.translation import gettext as _

from .capabilities import build_capability_context
from .constants import (
    DEFAULT_OPENAI_MODEL,
    DEFAULT_OPENAI_TIMEOUT_SECONDS,
    ENV_OPENAI_API_KEY_SETTING,
    MODE_ADMIN,
    SAFETY_SYSTEM_PROMPT,
)
from .skills import get_default_skill_registry

logger = logging.getLogger(__name__)


def _resolve_api_key(site_settings) -> str:
    return (
        getattr(settings, ENV_OPENAI_API_KEY_SETTING, "")
        or getattr(settings, "OPENAI_API_KEY", "")
        or site_settings.openai_api_key
        or ""
    ).strip()


def _resolve_system_prompt(site_settings, mode: str) -> str:
    if mode == MODE_ADMIN:
        return (site_settings.admin_system_prompt or "").strip()
    return (site_settings.customer_system_prompt or "").strip()


def _build_skill_system_messages(
    site_settings,
    mode: str,
    user_message: str,
    request=None,
    capability_context=None,
    registry=None,
) -> List[dict]:
    site = getattr(site_settings, "site", None)
    registry = registry or get_default_skill_registry()
    max_items = max(int(getattr(site_settings, "max_skill_context_items", 0) or 0), 1)
    messages: List[dict] = []

    for skill in registry.get_enabled_skills(
        site, site_settings, mode, capability_context=capability_context
    ):
        match = skill.match(user_message, request, site, mode, site_settings)
        if not match.matched:
            continue
        result = skill.build_context(user_message, request, site, mode, site_settings)
        if result.instruction:
            messages.append({"role": "system", "content": result.instruction})
        if result.items:
            rendered_items = "\n".join(
                f"- {item}" for item in result.items[:max_items] if item
            )
            if rendered_items:
                messages.append(
                    {
                        "role": "system",
                        "content": f"Skill context ({skill.key}):\n{rendered_items}",
                    }
                )
    return messages


def _build_messages(
    site_settings,
    mode: str,
    context_messages: Iterable,
    user_message: str,
    request=None,
    capability_context=None,
    registry=None,
) -> List[dict]:
    capability_context = capability_context or build_capability_context(
        mode=mode,
        settings_obj=site_settings,
        request=request,
        business_mode_requested=False,
    )
    messages: List[dict] = []
    messages.append({"role": "system", "content": SAFETY_SYSTEM_PROMPT})
    system_prompt = _resolve_system_prompt(site_settings, mode)
    if system_prompt:
        messages.append({"role": "system", "content": system_prompt})
    if capability_context.business_skills_allowed:
        business_instructions = (
            getattr(site_settings, "business_assistant_instructions", "") or ""
        ).strip()
        if business_instructions:
            messages.append({"role": "system", "content": business_instructions})
        messages.append(
            {
                "role": "system",
                "content": _(
                    "Business Assistant Mode is active. Only explicitly enabled business skills may be used. Treat all business-mode context as internal and permissioned. Remain read-only unless a future explicit approval workflow says otherwise. Do not claim to edit, submit, send, save, publish, or execute anything."
                ),
            }
        )
    messages.extend(
        _build_skill_system_messages(
            site_settings,
            mode,
            user_message,
            request=request,
            capability_context=capability_context,
            registry=registry,
        )
    )

    for message in context_messages:
        if message.role not in {"user", "assistant", "system"}:
            continue
        messages.append({"role": message.role, "content": message.content})

    messages.append({"role": "user", "content": user_message})
    return messages


def generate_reply(
    site_settings,
    mode: str,
    context_messages: Iterable,
    user_message: str,
    request=None,
    capability_context=None,
) -> Tuple[str, dict]:
    api_key = _resolve_api_key(site_settings)
    model = (site_settings.openai_model or DEFAULT_OPENAI_MODEL).strip()
    timeout = getattr(
        settings, "AI_ASSISTANT_OPENAI_TIMEOUT", DEFAULT_OPENAI_TIMEOUT_SECONDS
    )

    if not api_key:
        logger.warning(
            "ai_assistant.provider_unavailable",
            extra={
                "assistant_mode": mode,
                "provider": "openai",
                "reason": "missing_api_key",
            },
        )
        return (
            _(
                "The assistant is not configured right now. Please contact a staff member if you still need help."
            ),
            {
                "provider": "openai",
                "model": model,
                "fallback": "missing_api_key",
            },
        )

    try:
        from openai import OpenAI

        client = OpenAI(api_key=api_key, timeout=timeout)
        response = client.chat.completions.create(
            model=model,
            messages=_build_messages(
                site_settings,
                mode,
                context_messages,
                user_message,
                request=request,
                capability_context=capability_context,
            ),
            temperature=0.3,
        )
        choice = response.choices[0]
        text = (choice.message.content or "").strip()
        usage = getattr(response, "usage", None)
        token_count = getattr(usage, "total_tokens", None) if usage else None
        if not text:
            text = _("I could not generate a response. Please try again.")
        logger.info(
            "ai_assistant.provider_success",
            extra={
                "assistant_mode": mode,
                "provider": "openai",
                "model": model,
                "tokens": token_count,
            },
        )
        return text, {"provider": "openai", "model": model, "tokens": token_count}
    except Exception as exc:  # pragma: no cover
        logger.exception(
            "ai_assistant.provider_exception",
            extra={
                "assistant_mode": mode,
                "provider": "openai",
                "model": model,
                "error_type": type(exc).__name__,
            },
        )
        return (
            _(
                "I cannot answer right now. Please try again in a moment or contact a staff member if the issue continues."
            ),
            {
                "provider": "openai",
                "model": model,
                "fallback": "exception",
                "error_type": type(exc).__name__,
            },
        )
