from typing import Any

from fastmcp.exceptions import ToolError
from mcp.types import ToolAnnotations

from mcp_eregistrations_smartlink.audit.context import get_user_email_from_token
from mcp_eregistrations_smartlink.audit.logger import SlAuditLogger
from mcp_eregistrations_smartlink.audit.models import SlObjectType
from mcp_eregistrations_smartlink.server import mcp
from mcp_eregistrations_smartlink.smartlink_client import (
    SmartLinkClient,
    SmartLinkClientError,
    translate_error,
)
from mcp_eregistrations_smartlink.tools.audits import _validate_uuid

_ACTION_UPDATABLE_FIELDS = {
    "name",
    "description",
    "targetUrl",
    "httpMethod",
    "requestTransform",
    "responseTransform",
    "headers",
    "authType",
    "authConfig",
    "requestContentType",
    "targetUrlTransform",
    "runTargetRequest",
    "enabled",
}


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def smartlink_action_get(
    action_id: str,
    instance: str | None = None,
) -> dict[str, Any]:
    """Get SmartLink action definition by ID.

    Returns action config: target URL, transforms, auth type, headers.

    Args:
        action_id: Action identifier.
        instance: Instance name (e.g. "elsalvador-dev").

    Returns:
        dict with id, name, targetUrl, httpMethod, transforms, authType.
    """
    _validate_uuid(action_id, "Action ID")
    try:
        async with SmartLinkClient.for_instance(instance) as client:
            result: dict[str, Any] = await client.get(f"/actions/{action_id}")
            return result
    except SmartLinkClientError as e:
        raise translate_error(e, resource_type="action", resource_id=action_id) from e


@mcp.tool()
async def smartlink_action_update(
    action_id: str,
    updates: dict[str, Any],
    instance: str | None = None,
) -> dict[str, Any]:
    """Update SmartLink action configuration. Audited write operation.

    Captures previous state for rollback via smartlink_rollback.

    Args:
        action_id: Action identifier.
        updates: Fields to update (allowed: name, description, targetUrl,
            httpMethod, requestTransform, responseTransform, headers,
            authType, authConfig, requestContentType, targetUrlTransform,
            runTargetRequest, enabled).
        instance: Instance name (e.g. "elsalvador-dev").

    Returns:
        dict with action_id, updated_fields, previous_values, audit_id.
    """
    _validate_uuid(action_id, "Action ID")
    filtered = {k: v for k, v in updates.items() if k in _ACTION_UPDATABLE_FIELDS}
    if not filtered:
        raise ToolError(
            f"No valid fields to update. "
            f"Allowed: {', '.join(sorted(_ACTION_UPDATABLE_FIELDS))}"
        )

    logger = SlAuditLogger.for_instance(instance)
    audit_id: str | None = None
    try:
        async with SmartLinkClient.for_instance(instance) as client:
            # Verify resource exists BEFORE creating audit entry
            previous = await client.get(f"/actions/{action_id}")

            user_email = get_user_email_from_token(instance)
            audit_id = await logger.record_pending(
                user_email,
                "update",
                SlObjectType.ACTION,
                {"action_id": action_id, "fields": list(filtered.keys())},
                object_id=action_id,
            )
            await logger.save_rollback_state(
                audit_id, SlObjectType.ACTION, action_id, previous
            )
            previous_values = {k: previous.get(k) for k in filtered}

            # Apply update
            result = await client.put(f"/actions/{action_id}", data=filtered)

            outcome = {
                "action_id": action_id,
                "updated_fields": list(filtered.keys()),
                "previous_values": previous_values,
                "audit_id": audit_id,
                "status": "updated",
            }
            await logger.mark_success(audit_id, outcome)
            return {**outcome, "action": result}
    except SmartLinkClientError as e:
        if audit_id is not None:
            await logger.mark_failed(audit_id, str(e))
        raise translate_error(e, resource_type="action", resource_id=action_id) from e
