"""Command for sending a message and waiting for the final response."""

from dataclasses import dataclass
from typing import Callable
from uuid import UUID

from alloy_runtime_sdk.api_client.client import ApiClient
from alloy_runtime_types.dtos.generation import (
    AgentSource,
    DirectModelSource,
    GenerateTextRequest,
)
from alloy_runtime_types.enums.provider import Provider
from alloy_runtime_sdk.logging.config import get_logger

from cli.infrastructure.injection.parser import has_injections
from cli.infrastructure.injection.resolver import ResolvedMessage
from cli.tui.chat.commands.base import CommandResult
from cli.tui.chat.services.injection import InjectionService
from cli.tui.chat.store import ChatStore

logger = get_logger(__name__)


@dataclass
class SendMessageCommand:
    """Command to send a user message and await the final AI response.

    Supports two modes:
    - Agent mode: Uses agent_id to determine model and system instruction
    - Model mode: Uses provider_key/provider_model_name/system_instruction directly

    Also supports injection resolution:
    - @template('name') - Inject rendered template
    - @text('uuid') - Inject text content
    - @json('uuid') - Inject JSON content
    - @schema('name') - Inject schema definition

    This command:
    1. Resolves any injection patterns in the message
    2. Transitions store to SENDING phase
    3. Builds and sends the generation request
    4. Waits for the final response from the non-streaming API
    5. Leaves the store in SENDING on success so the caller can reload persisted
       messages before clearing the optimistic pending state

    Attributes:
        client: Server client for API calls.
        store: Chat store for state management.
        session_id: ID of the current chat session.
        user_message: The user's message to send.
        agent_id: ID of the agent to use for generation (agent mode).
        provider_key: Provider key (model mode).
        provider_model_name: Model name (model mode).
        system_instruction: System instruction (model mode).
        injection_service: Optional service for resolving injection patterns.
        on_injection_resolved: Callback invoked with resolution result.
    """

    client: ApiClient
    store: ChatStore
    session_id: UUID
    user_message: str
    # Agent mode
    agent_id: UUID | None = None
    # Model mode
    provider_key: str | None = None
    provider_model_name: str | None = None
    system_instruction: str | None = None
    # Injection support
    injection_service: InjectionService | None = None
    on_injection_resolved: Callable[[ResolvedMessage], None] | None = None

    async def execute(self) -> CommandResult[None]:
        """Execute the send message operation.

        Returns:
            CommandResult indicating success or failure.
        """
        message_to_send = self.user_message

        # Resolve injections if service is available and message has patterns
        if self.injection_service and has_injections(self.user_message):
            try:
                resolved = await self.injection_service.resolve_message(
                    self.user_message
                )

                # Check for errors
                if resolved.has_errors:
                    error_messages = [e.message for e in resolved.errors]
                    return CommandResult[None](
                        success=False,
                        error=f"Injection resolution failed: {error_messages[0]}",
                    )

                # Notify callback about successful resolution
                if self.on_injection_resolved:
                    self.on_injection_resolved(resolved)

                # Use resolved text
                message_to_send = resolved.text

                logger.debug(
                    "injection_resolved",
                    extra={
                        "fragments_used": len(resolved.fragments_used),
                        "content_used": len(resolved.content_used),
                        "schemas_used": len(resolved.schemas_used),
                    },
                )

            except Exception as e:
                logger.error(
                    "injection_resolution_error",
                    extra={"error": str(e)},
                )
                return CommandResult[None](
                    success=False,
                    error=f"Failed to resolve injections: {e}",
                )

        # Start generation with the user message shown optimistically
        # Note: We show the original message, not the resolved one
        self.store.start_generation(user_message=self.user_message)
        try:
            # Build model source based on mode
            if self.agent_id is not None:
                model_source = AgentSource(agent=self.agent_id)
            elif self.provider_key and self.provider_model_name:
                model_source = DirectModelSource(
                    provider_key=Provider(self.provider_key),
                    provider_model_name=self.provider_model_name,
                )
            else:
                self.store.finish_generation()
                return CommandResult[None](
                    success=False,
                    error="No agent or model configured for this session",
                )

            request = GenerateTextRequest(
                model_source=model_source,
                user_message=message_to_send,  # Use resolved message
                stream=False,
                tags=[],
                chat_session_id=self.session_id,
                # Only include system instruction for model mode
                system_instruction=self.system_instruction
                if self.agent_id is None
                else None,
            )

            await self.client.generate_text(request)

            return CommandResult[None](success=True, data=None)

        except Exception as e:
            logger.warning("send_message_failed", error=str(e))
            self.store.finish_generation()
            return CommandResult[None](success=False, error=str(e))
