"""Agent — a configured AgentCore with retry/timeout logic.

An Agent wraps an AgentCore instance with a persona (system prompt)
and operational concerns (retries, timeouts) from AgentConfig.
"""

from __future__ import annotations

import asyncio
import logging
from typing import TYPE_CHECKING, AsyncIterator

from cliver.media import MediaContent
from cliver.messages import CLIverMessageChunk
from cliver.provider import CLIverResponse

if TYPE_CHECKING:
    from cliver.config import AgentConfig
    from cliver.llm.agent_core import AgentCore

logger = logging.getLogger(__name__)


class Agent:
    """A configured AgentCore with retry/timeout logic.

    One Agent per persona — wraps an AgentCore and adds:
    - System prompt from config (role, system_prompt, skills)
    - Retry on failure (config.max_retries)
    - Timeout (config.timeout_s)

    Return types are the same as AgentCore — no custom result wrappers.
    """

    def __init__(
        self,
        name: str,
        config: "AgentConfig",
        agent_core: "AgentCore",
    ):
        self.name = name
        self.config = config
        self._core = agent_core

    # ── Public API ────────────────────────────────────────────

    async def chat(
        self,
        prompt: str,
        *,
        media: list[MediaContent] | None = None,
        system_prompt: str | None = None,
        **kwargs,
    ) -> CLIverResponse:
        """Run with retry/timeout.  Returns CLIverResponse (same as AgentCore)."""
        sp = self._merge_system_prompt(system_prompt)
        timeout = self.config.timeout_s or 300
        last_error = None

        for attempt in range(1 + self.config.max_retries):
            try:
                return await asyncio.wait_for(
                    self._core.chat(
                        user_input=prompt,
                        system_prompt=sp,
                        media=media,
                        **kwargs,
                    ),
                    timeout=timeout,
                )
            except asyncio.TimeoutError:
                last_error = f"Timeout after {timeout}s (attempt {attempt + 1})"
                logger.warning("%s: %s", self.name, last_error)
            except Exception as e:
                last_error = str(e)
                if attempt < self.config.max_retries:
                    logger.warning("%s: retry %d/%d: %s", self.name, attempt + 1, self.config.max_retries, e)
                    continue
                break

        return CLIverResponse(
            message=__import__("cliver.messages").messages.CLIverMessage(
                role="assistant",
                content=f"Error: {last_error}",
            ),
        )

    async def stream(
        self,
        prompt: str,
        *,
        media: list[MediaContent] | None = None,
        system_prompt: str | None = None,
        **kwargs,
    ) -> AsyncIterator[CLIverMessageChunk]:
        """Streaming — no retry for streams.  Yields CLIverMessageChunk."""
        sp = self._merge_system_prompt(system_prompt)
        async for chunk in self._core.stream(
            user_input=prompt,
            system_prompt=sp,
            media=media,
            **kwargs,
        ):
            yield chunk

    async def generate(
        self,
        prompt: str,
        *,
        media_type: str = "image",
        media: list[MediaContent] | None = None,
        output_dir: str | None = None,
        **options,
    ) -> CLIverResponse:
        """Generate media.  Returns CLIverResponse with media field."""
        return await self._core.generate(
            prompt=prompt,
            media_type=media_type,
            media=media,
            output_dir=output_dir,
            **options,
        )

    # ── Helpers ────────────────────────────────────────────────

    def _merge_system_prompt(self, builtin: str | None) -> str | None:
        """Merge the builtin system prompt with the agent's persona.

        The builtin prompt (model inventory, tools, guidelines) always comes
        first.  The agent's persona from AgentConfig is appended if set.
        """
        persona = self.config.system_prompt
        if not persona:
            return builtin
        role_section = f"# Role\n\n{persona}"
        if builtin:
            return f"{builtin}\n\n{role_section}"
        return role_section

    async def cleanup(self) -> None:
        pass
