"""Agent update modal for editing existing agents.

Provides a comprehensive modal screen that allows users to edit an existing agent
with all its current fields pre-populated.
"""

from typing import Any

from textual import work
from textual.widgets import Input, TextArea

from alloy_runtime_sdk.api_client.client import ApiClient
from alloy_runtime_types.dtos.agents import (
    GetAgentResponse,
    UpdateAgentRequest,
)
from alloy_runtime_types.dtos.models import ModelListItemResponse
from alloy_runtime_types.enums.ownership_scope import OwnershipScope

from cli.infrastructure.forms.schema_picker import SchemaPicker
from cli.infrastructure.forms.tag_picker import TagPicker
from cli.infrastructure.forms.template_picker import TemplatePicker
from cli.tui.widgets.agent_form_modal import AgentFormModal, AgentFormResult


class AgentUpdateModal(AgentFormModal):
    """Modal for updating an existing agent.

    Pre-populates all form fields with the current agent configuration.
    Scope cannot be changed after creation.

    Usage:
        def on_agent_updated(result: AgentFormResult | None):
            if result is not None:
                self.notify(f"Updated agent: {result.agent_name}")
                self._do_search("")  # Refresh list

        client = await self.app.store.get_client()
        agent = await client.get_agent(agent_id)
        self.app.push_screen(
            AgentUpdateModal(client=client, agent=agent),
            on_agent_updated
        )
    """

    def __init__(
        self,
        client: ApiClient,
        agent: GetAgentResponse,
        **kwargs: Any,
    ) -> None:
        """Initialize the agent update modal.

        Args:
            client: ApiClient instance for API calls
            agent: The agent to edit (full details from get_agent)
        """
        super().__init__(client=client, **kwargs)
        self._agent = agent
        self._initial_system_instruction_selection: dict[str, str] | None = None

    def _get_modal_title(self) -> str:
        """Return the modal title."""
        return f"Edit Agent: {self._agent.name}"

    def _get_submit_button_text(self) -> str:
        """Return the submit button text."""
        return "Update Agent"

    def _is_scope_editable(self) -> bool:
        """Return whether the scope can be changed (not allowed for updates)."""
        return False

    def on_mount(self) -> None:
        """Populate form fields with existing agent data."""
        super().on_mount()
        self._populate_form_async()

    @work(exclusive=True)
    async def _populate_form_async(self) -> None:
        """Populate all form fields with the existing agent data (async for API calls)."""
        agent = self._agent

        # Basic fields
        self._set_name(agent.name)
        self._set_description(agent.description)

        # Scope (read-only, just display)
        scope = self._infer_scope_from_agent(agent)
        self._set_scope(scope)

        # Models - need to convert AgentModelResponse to ModelListItemResponse-like objects
        if agent.models:
            model_items = [
                ModelListItemResponse(
                    provider_key=m.provider_key.value,  # Use .value to get string from enum
                    provider_model_name=m.provider_model_name,
                    is_active=True,  # Assume active since it's assigned to agent
                    input_modalities=["text"],  # Default, actual not stored on agent
                    output_modalities=["text"],  # Default, actual not stored on agent
                )
                for m in sorted(agent.models, key=lambda x: x.preference_order)
            ]
            self._set_models(model_items)

        # Tags - need to convert AgentTagResponse to dict format
        if agent.tags:
            tag_items = [
                {
                    "tag_path": t.tag_path,
                    "display_name": t.display_name,
                    "id": str(t.tag_id),
                }
                for t in agent.tags
            ]
            self._set_tags(tag_items)

        # Base agent
        if agent.base_agent_id:
            self._set_base_agent(str(agent.base_agent_id), "Base Agent")

        template_data: dict[str, Any] | None = None
        if agent.system_instruction_template_id:
            template_data = await self._fetch_latest_template_data(
                str(agent.system_instruction_template_id)
            )
        elif agent.system_instruction_version_id:
            template_data = await self._fetch_pinned_template_data(
                str(agent.system_instruction_version_id)
            )

        self._initial_system_instruction_selection = self._normalize_template_selection(
            template_data
        )
        if template_data:
            template_picker = self.query_one("#system-template-picker", TemplatePicker)
            template_picker.set_initial_selection(template_data)

        # Output schema - fetch actual data
        if agent.output_schema_id:
            schema_data = await self._fetch_schema_data(str(agent.output_schema_id))
            if schema_data:
                # Use the set_initial_selection with full data for proper display
                output_schema_picker = self.query_one(
                    "#output-schema-picker", SchemaPicker
                )
                output_schema_picker.set_initial_selection(schema_data)

        # Generation params
        if agent.generation_params:
            self._set_generation_params(agent.generation_params)

        # Default headers
        if agent.default_request_headers:
            self._set_default_headers(agent.default_request_headers)

        # Tools
        if agent.tools:
            self._set_tools(agent.tools)

    def _infer_scope_from_agent(self, agent: GetAgentResponse) -> OwnershipScope:
        """Infer the scope from agent ownership fields."""
        if agent.user_id:
            return OwnershipScope.USER
        elif agent.organization_id:
            return OwnershipScope.ORGANIZATION
        else:
            return OwnershipScope.GLOBAL

    async def _fetch_pinned_template_data(
        self, version_id: str
    ) -> dict[str, Any] | None:
        try:
            from uuid import UUID

            response = await self._client.get_template_by_version(UUID(version_id))
            matching_version = next(
                (v for v in response.versions if str(v.id) == version_id), None
            )
            version_number = matching_version.version if matching_version else "?"

            return {
                "selection_mode": "pinned",
                "template_id": str(response.template.id),
                "version_id": version_id,
                "latest_version_id": version_id,
                "name": response.template.name,
                "latest_version_number": version_number,
                "visibility_id": response.template.visibility_id,
                "content_type_id": response.template.content_type_id,
            }
        except Exception:
            return {
                "selection_mode": "pinned",
                "template_id": "",
                "version_id": version_id,
                "latest_version_id": version_id,
                "name": f"Template ({version_id})",
                "latest_version_number": "?",
                "visibility_id": "private",
                "content_type_id": "",
            }

    async def _fetch_latest_template_data(
        self, template_id: str
    ) -> dict[str, Any] | None:
        try:
            response = await self._client.get_template(template_id)
            latest_version = max(response.versions, key=lambda version: version.version)

            return {
                "selection_mode": "latest",
                "template_id": template_id,
                "version_id": str(latest_version.id),
                "latest_version_id": str(latest_version.id),
                "name": response.template.name,
                "latest_version_number": latest_version.version,
                "visibility_id": response.template.visibility_id,
                "content_type_id": response.template.content_type_id,
            }
        except Exception:
            return {
                "selection_mode": "latest",
                "template_id": template_id,
                "latest_version_id": "",
                "name": f"Template ({template_id})",
                "latest_version_number": "?",
                "visibility_id": "private",
                "content_type_id": "",
            }

    def _normalize_template_selection(
        self, template_data: dict[str, Any] | None
    ) -> dict[str, str] | None:
        if template_data is None:
            return None

        normalized = {
            "selection_mode": str(template_data.get("selection_mode", "latest"))
        }

        template_id = template_data.get("template_id") or template_data.get("id")
        if template_id:
            normalized["template_id"] = str(template_id)

        version_id = template_data.get("version_id") or template_data.get(
            "latest_version_id"
        )
        if version_id:
            normalized["version_id"] = str(version_id)

        return normalized

    async def _fetch_schema_data(self, schema_id: str) -> dict[str, Any] | None:
        """Fetch the schema data for a given schema ID.

        Args:
            schema_id: Schema UUID

        Returns:
            Schema data dict for display, or None if fetch fails
        """
        try:
            response = await self._client.get_schema(schema_id)
            # Build data dict that matches what the picker's formatter expects
            return {
                "id": schema_id,
                "schema_name": response.schema_name,
                "version": response.version,
                "schema_format_id": response.schema_format_id,
                "user_id": str(response.user_id) if response.user_id else None,
                "organization_id": str(response.organization_id)
                if response.organization_id
                else None,
                "description": response.description,
            }
        except Exception:
            # If fetch fails, create a minimal fallback dict
            return {
                "id": schema_id,
                "schema_name": f"Schema ({schema_id})",
                "version": "?",
                "schema_format_id": "unknown",
                "user_id": None,
                "organization_id": None,
                "description": None,
            }

    async def _submit_form(self) -> AgentFormResult:
        """Submit the form to update the agent."""
        request = self._build_update_request()
        response = await self._client.update_agent(self._agent.id, request)

        return AgentFormResult(
            agent_id=str(response.id),
            agent_name=response.name,
            is_update=True,
        )

    def _build_update_request(self) -> UpdateAgentRequest:
        """Build the UpdateAgentRequest from form fields."""
        # Name
        name = self.query_one("#name-input", Input).value.strip()

        # Models
        model_configs = self._build_model_configs()

        # Tags
        tag_picker = self.query_one("#tag-picker", TagPicker)
        tags = tag_picker.selected_tags

        # Description
        description = self.query_one("#description-input", Input).value.strip() or None

        request_kwargs: dict[str, Any] = {}

        template_picker = self.query_one("#system-template-picker", TemplatePicker)
        current_system_instruction_selection = self._normalize_template_selection(
            template_picker.selected_item
        )
        if (
            current_system_instruction_selection
            != self._initial_system_instruction_selection
        ):
            if current_system_instruction_selection is None:
                request_kwargs["system_instruction_template"] = None
            elif current_system_instruction_selection["selection_mode"] == "pinned":
                request_kwargs["system_instruction_version_id"] = self._parse_uuid(
                    current_system_instruction_selection.get("version_id"),
                    "System Template",
                )
            else:
                request_kwargs["system_instruction_template"] = self._parse_uuid(
                    current_system_instruction_selection.get("template_id"),
                    "System Template",
                )

        # Schemas
        output_schema_picker = self.query_one("#output-schema-picker", SchemaPicker)
        output_schema_id = self._parse_uuid(
            output_schema_picker.selected_id, "Output Schema"
        )

        # Output schema from JSON builder
        output_schema = self._get_output_schema()

        # Generation parameters
        gen_params = self._build_generation_params()

        # Default headers
        default_headers = self._parse_json(
            self.query_one("#default-headers-textarea", TextArea).text,
            "Default Headers",
        )

        return UpdateAgentRequest(
            name=name,
            models=model_configs,
            tags=tags,
            description=description,
            output_schema_id=output_schema_id,
            output_schema=output_schema,
            default_request_headers=default_headers,
            generation_params=gen_params,
            tools=self._build_tools(),
            **request_kwargs,
        )
