"""MCP tools for BPA cost operations.

This module provides tools for creating, updating, and deleting BPA costs.
Costs define fees that applicants must pay for a registration.
Two cost types are supported: fixed costs and formula-based costs.

Write operations follow the audit-before-write pattern:
1. Validate parameters (pre-flight, no audit record if validation fails)
2. Create PENDING audit record
3. Execute BPA API call
4. Update audit record to SUCCESS or FAILED

API Endpoints used:
- POST /registration/{registration_id}/fixcost - Create fixed cost
- POST /registration/{registration_id}/formulacost - Create formula cost
- PUT /cost - Update cost
- DELETE /cost/{cost_id} - Delete cost
"""

from __future__ import annotations

import time
from typing import Any

from fastmcp.exceptions import ToolError

from mcp_eregistrations_bpa.audit.context import (
    NotAuthenticatedError,
    get_current_user_email,
)
from mcp_eregistrations_bpa.audit.logger import AuditLogger
from mcp_eregistrations_bpa.bpa_client import BPAClient
from mcp_eregistrations_bpa.bpa_client.errors import (
    BPAClientError,
    BPANotFoundError,
    translate_error,
)
from mcp_eregistrations_bpa.config import resolve_db_path

__all__ = [
    "cost_create_fixed",
    "cost_create_formula",
    "cost_update",
    "cost_delete",
    "register_cost_tools",
]


# Module-level cache: {instance_url: (currencies_list, timestamp)}
_currency_cache: dict[str, tuple[list[dict[str, Any]], float]] = {}
_CURRENCY_CACHE_TTL = 300.0  # 5 minutes


async def _resolve_currency_id(client: Any, currency_code: str) -> str:
    """Resolve a currency code (e.g. "USD") to its BPA database UUID.

    Results are cached per instance for 5 minutes to avoid repeated lookups
    during batch operations.

    Args:
        client: Active BPAClient instance.
        currency_code: Currency code like "USD", "EUR", "LSL".

    Returns:
        The currency UUID string.

    Raises:
        ToolError: If currency code not found or no currencies enabled.
    """
    if not currency_code:
        raise ToolError("'currency' is required (e.g. 'USD', 'EUR').")

    cache_key = getattr(client, "base_url", "__default__")
    now = time.monotonic()

    # Check cache
    if cache_key in _currency_cache:
        cached_currencies, cached_at = _currency_cache[cache_key]
        if now - cached_at < _CURRENCY_CACHE_TTL:
            currencies = cached_currencies
        else:
            currencies = None
    else:
        currencies = None

    # Fetch if not cached
    if currencies is None:
        currencies = await client.get(
            "/currency/enabled",
            resource_type="currency",
        )
        # Only cache non-empty results to avoid persisting transient failures
        if currencies:
            _currency_cache[cache_key] = (currencies, now)

    if not currencies:
        raise ToolError(
            "No currencies are enabled on this BPA instance. "
            "An administrator must enable at least one currency."
        )

    # Match by code (case-insensitive)
    code_upper = currency_code.upper()
    for cur in currencies:
        if cur.get("code", "").upper() == code_upper:
            return str(cur["id"])

    # Not found — list available codes for the AI agent
    available = [c.get("code", "?") for c in currencies]
    raise ToolError(
        f"Currency '{currency_code}' not found. "
        f"Available currencies: {', '.join(available)}."
    )


def _transform_cost_response(
    data: dict[str, Any], cost_type: str | None = None
) -> dict[str, Any]:
    """Transform cost API response from camelCase to snake_case.

    Args:
        data: Raw API response with camelCase keys.
        cost_type: Optional cost type override ("fixed" or "formula").

    Returns:
        dict: Transformed response with snake_case keys.
    """
    result: dict[str, Any] = {
        "id": data.get("id"),
        "name": data.get("name"),
        "description": data.get("additionalInformation") or data.get("description"),
        "cost_type": cost_type or data.get("costType") or data.get("type"),
        "registration_id": data.get("registrationId"),
    }

    # Include type-specific fields (API uses fixValue, currencyId, formulaCostItems)
    if data.get("fixValue") is not None:
        result["amount"] = data.get("fixValue")
    elif data.get("amount") is not None:
        result["amount"] = data.get("amount")
    # Extract formula from formulaCostItems array or direct formula field
    formula_items = data.get("formulaCostItems")
    if formula_items and len(formula_items) > 0:
        result["formula"] = formula_items[0].get("infixFormula")
    elif data.get("formula"):
        result["formula"] = data.get("formula")
    # Extract currency: prefer code from embedded object, fall back to ID
    currency_obj = data.get("currency")
    if isinstance(currency_obj, dict):
        result["currency_id"] = currency_obj.get("id")
        result["currency_code"] = currency_obj.get("code")
    elif data.get("currencyId"):
        result["currency_id"] = data.get("currencyId")
    if data.get("variables"):
        result["variables"] = data.get("variables")

    return result


def _validate_cost_create_fixed_params(
    registration_id: str | int,
    name: str,
    amount: float,
    currency: str,
    description: str | None,
) -> dict[str, Any]:
    """Validate cost_create_fixed parameters (pre-flight).

    Returns validated params dict or raises ToolError if invalid.
    No audit record is created for validation failures.

    Args:
        registration_id: Parent registration ID (required).
        name: Cost name (required).
        amount: Cost amount (required, must be >= 0).
        currency: Currency code (required).
        description: Cost description (optional).

    Returns:
        dict: Validated parameters ready for API call.

    Raises:
        ToolError: If validation fails.
    """
    errors = []

    if not registration_id:
        errors.append("'registration_id' is required")

    if not name or not name.strip():
        errors.append("'name' is required and cannot be empty")

    if name and len(name.strip()) > 255:
        errors.append("'name' must be 255 characters or less")

    if amount is None:
        errors.append("'amount' is required")
    elif amount < 0:
        errors.append("'amount' must be a non-negative number")

    if not currency or not currency.strip():
        errors.append("'currency' is required (e.g. 'USD', 'EUR')")

    if errors:
        error_msg = "; ".join(errors)
        raise ToolError(f"Cannot create fixed cost: {error_msg}.")

    # API uses fixValue (not amount), costType discriminator
    # currencyId is resolved from currency code in the async caller
    params: dict[str, Any] = {
        "name": name.strip(),
        "fixValue": amount,
        "costType": "FIX",
    }
    if description:
        params["additionalInformation"] = description.strip()

    return params


async def cost_create_fixed(
    registration_id: str | int,
    name: str,
    amount: float,
    currency: str = "USD",
    description: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Create fixed cost for a registration. Audited write operation.

    Args:
        registration_id: Parent registration ID.
        name: Cost name.
        amount: Fixed amount (must be >= 0).
        currency: Currency code (default: "USD").
        description: Optional description.
        instance: Instance profile name (from instance_list).

    Returns:
        dict with id, name, amount, currency_id, currency_code, cost_type,
        registration_id, audit_id.
    """
    # Pre-flight validation (no audit record for validation failures)
    validated_params = _validate_cost_create_fixed_params(
        registration_id, name, amount, currency, description
    )

    # Get authenticated user for audit (before any API calls)
    try:
        user_email = get_current_user_email(instance=instance)
    except NotAuthenticatedError as e:
        raise ToolError(str(e))

    # Use single BPAClient connection for all operations
    try:
        async with BPAClient.for_instance(instance) as client:
            # Verify parent registration exists before creating audit record
            try:
                await client.get(
                    "/registration/{registration_id}",
                    path_params={"registration_id": registration_id},
                    resource_type="registration",
                    resource_id=registration_id,
                )
            except BPANotFoundError:
                raise ToolError(
                    f"Cannot create fixed cost: Registration '{registration_id}' "
                    "not found. Use 'registration_list' to see available registrations."
                )

            # Resolve currency code to database UUID
            currency_id = await _resolve_currency_id(client, currency)
            validated_params["currencyId"] = currency_id

            # Create audit record BEFORE API call (audit-before-write pattern)
            audit_logger = AuditLogger(db_path=resolve_db_path(instance))
            audit_id = await audit_logger.record_pending(
                user_email=user_email,
                operation_type="create",
                object_type="cost",
                params={
                    "registration_id": str(registration_id),
                    "cost_type": "fixed",
                    **validated_params,
                },
            )

            try:
                cost_data = await client.post(
                    "/registration/{registration_id}/fixcost",
                    path_params={"registration_id": registration_id},
                    json=validated_params,
                    resource_type="cost",
                )

                # Save rollback state (for create, save ID to enable deletion)
                created_id = cost_data.get("id")
                await audit_logger.save_rollback_state(
                    audit_id=audit_id,
                    object_type="cost",
                    object_id=str(created_id),
                    previous_state={
                        "id": created_id,
                        "name": cost_data.get("name"),
                        "fixValue": cost_data.get("fixValue"),
                        "costType": "FIX",
                        "registrationId": str(registration_id),
                        "_operation": "create",  # Marker for rollback to DELETE
                    },
                )

                # Mark audit as success
                await audit_logger.mark_success(
                    audit_id,
                    result={
                        "cost_id": cost_data.get("id"),
                        "name": cost_data.get("name"),
                        "amount": validated_params["fixValue"],
                        "registration_id": str(registration_id),
                    },
                )

                result = _transform_cost_response(cost_data, cost_type="fixed")
                # Explicitly set registration_id from function parameter
                result["registration_id"] = registration_id
                result["audit_id"] = audit_id
                return result

            except BPAClientError as e:
                # Mark audit as failed
                await audit_logger.mark_failed(audit_id, str(e))
                raise translate_error(e, resource_type="cost")

    except ToolError:
        raise
    except BPAClientError as e:
        raise translate_error(
            e, resource_type="registration", resource_id=registration_id
        )


def _validate_cost_create_formula_params(
    registration_id: str | int,
    name: str,
    formula: str,
    currency: str,
    variables: dict[str, Any] | None,
    description: str | None,
) -> dict[str, Any]:
    """Validate cost_create_formula parameters (pre-flight).

    Returns validated params dict or raises ToolError if invalid.
    No audit record is created for validation failures.

    Args:
        registration_id: Parent registration ID (required).
        name: Cost name (required).
        formula: Cost formula expression (required).
        currency: Currency code (required).
        variables: Variable definitions for formula (optional).
        description: Cost description (optional).

    Returns:
        dict: Validated parameters ready for API call.

    Raises:
        ToolError: If validation fails.
    """
    errors = []

    if not registration_id:
        errors.append("'registration_id' is required")

    if not name or not name.strip():
        errors.append("'name' is required and cannot be empty")

    if name and len(name.strip()) > 255:
        errors.append("'name' must be 255 characters or less")

    if not formula or not formula.strip():
        errors.append("'formula' is required and cannot be empty")

    if not currency or not currency.strip():
        errors.append("'currency' is required (e.g. 'USD', 'EUR')")

    if errors:
        error_msg = "; ".join(errors)
        raise ToolError(
            f"Cannot create formula cost: {error_msg}. Check required fields."
        )

    # Cost entity is created first; formulaCostItems are saved separately
    # via POST /formulacostitem (they're @Transient on FormulaCost).
    params: dict[str, Any] = {
        "name": name.strip(),
        "costType": "FORMULA",
    }
    if description:
        params["additionalInformation"] = description.strip()

    # Store formula for the separate POST step
    params["_formula_items"] = [
        {
            "infixFormula": formula.strip(),
            "sortOrder": 0,
        }
    ]

    return params


async def cost_create_formula(
    registration_id: str | int,
    name: str,
    formula: str,
    currency: str = "USD",
    variables: dict[str, Any] | None = None,
    description: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Create formula-based cost for a registration. Audited write operation.

    Args:
        registration_id: Parent registration ID.
        name: Cost name.
        formula: Formula expression for cost calculation.
        currency: Currency code (default: "USD").
        variables: Optional variable definitions for formula.
        description: Optional description.
        instance: Instance profile name (from instance_list).

    Returns:
        dict with id, name, formula, variables, currency_id, currency_code,
        cost_type, registration_id, audit_id.
    """
    # Pre-flight validation (no audit record for validation failures)
    validated_params = _validate_cost_create_formula_params(
        registration_id, name, formula, currency, variables, description
    )

    # Get authenticated user for audit (before any API calls)
    try:
        user_email = get_current_user_email(instance=instance)
    except NotAuthenticatedError as e:
        raise ToolError(str(e))

    # Use single BPAClient connection for all operations
    try:
        async with BPAClient.for_instance(instance) as client:
            # Verify parent registration exists before creating audit record
            try:
                await client.get(
                    "/registration/{registration_id}",
                    path_params={"registration_id": registration_id},
                    resource_type="registration",
                    resource_id=registration_id,
                )
            except BPANotFoundError:
                raise ToolError(
                    f"Cannot create formula cost: Registration '{registration_id}' "
                    "not found. Use 'registration_list' to see available registrations."
                )

            # Resolve currency code to database UUID
            currency_id = await _resolve_currency_id(client, currency)
            validated_params["currencyId"] = currency_id

            # Extract formula items (stored separately via /formulacostitem)
            formula_items = validated_params.pop("_formula_items", [])

            # Create audit record BEFORE API call (audit-before-write pattern)
            audit_logger = AuditLogger(db_path=resolve_db_path(instance))
            audit_id = await audit_logger.record_pending(
                user_email=user_email,
                operation_type="create",
                object_type="cost",
                params={
                    "registration_id": str(registration_id),
                    "cost_type": "formula",
                    "formula_items": formula_items,
                    **validated_params,
                },
            )

            try:
                # Step 1: Create the FormulaCost entity (without items)
                cost_data = await client.post(
                    "/registration/{registration_id}/formulacost",
                    path_params={"registration_id": registration_id},
                    json=validated_params,
                    resource_type="cost",
                )

                created_id = cost_data.get("id")
                formula_str = formula_items[0]["infixFormula"] if formula_items else ""

                # Step 2: Create each FormulaCostItem separately
                # (formulaCostItems is @Transient on FormulaCost — must be
                # saved individually via POST /formulacostitem)
                for item in formula_items:
                    item_payload = {
                        "formulaCostId": created_id,
                        **item,
                    }
                    await client.post(
                        "/formulacostitem",
                        json=item_payload,
                        resource_type="cost",
                    )

                # Save rollback state (for create, save ID to enable deletion)
                await audit_logger.save_rollback_state(
                    audit_id=audit_id,
                    object_type="cost",
                    object_id=str(created_id),
                    previous_state={
                        "id": created_id,
                        "name": cost_data.get("name"),
                        "formulaCostItems": formula_items,
                        "costType": "FORMULA",
                        "registrationId": str(registration_id),
                        "_operation": "create",  # Marker for rollback to DELETE
                    },
                )

                # Mark audit as success
                await audit_logger.mark_success(
                    audit_id,
                    result={
                        "cost_id": created_id,
                        "name": cost_data.get("name"),
                        "formula": formula_str,
                        "registration_id": str(registration_id),
                    },
                )

                result = _transform_cost_response(cost_data, cost_type="formula")
                result["formula"] = formula_str
                result["registration_id"] = registration_id
                result["audit_id"] = audit_id
                return result

            except BPAClientError as e:
                # Mark audit as failed
                await audit_logger.mark_failed(audit_id, str(e))
                raise translate_error(e, resource_type="cost")

    except ToolError:
        raise
    except BPAClientError as e:
        raise translate_error(
            e, resource_type="registration", resource_id=registration_id
        )


def _validate_cost_update_params(
    cost_id: str | int,
    cost_type: str,
    name: str | None,
    fix_value: float | None,
    formula: str | None,
    currency: str | None,
    description: str | None,
) -> dict[str, Any]:
    """Validate cost_update parameters (pre-flight).

    Returns validated params dict or raises ToolError if invalid.

    Args:
        cost_id: ID of cost to update (required).
        cost_type: Type of cost ("FIX" or "FORMULA") (required).
        name: New name (optional).
        fix_value: New fixed value for fixed costs (optional).
        formula: New formula for formula costs (optional).
        currency: New currency code (optional, e.g. "USD").
        description: New description (optional).

    Returns:
        dict: Validated parameters ready for API call.

    Raises:
        ToolError: If validation fails.
    """
    errors = []

    if not cost_id:
        errors.append("'cost_id' is required")

    valid_cost_types = ["FIX", "FORMULA"]
    if not cost_type:
        errors.append("'cost_type' is required (FIX or FORMULA)")
    elif cost_type.upper() not in valid_cost_types:
        errors.append(f"'cost_type' must be one of: {', '.join(valid_cost_types)}")

    if name is not None and not name.strip():
        errors.append("'name' cannot be empty when provided")

    if name and len(name.strip()) > 255:
        errors.append("'name' must be 255 characters or less")

    if fix_value is not None and fix_value < 0:
        errors.append("'fix_value' must be a non-negative number when provided")

    if formula is not None and not formula.strip():
        errors.append("'formula' cannot be empty when provided")

    if currency is not None and not currency.strip():
        errors.append("'currency' cannot be empty when provided")

    # At least one field must be provided for update
    has_change = any(
        v is not None for v in [name, fix_value, formula, currency, description]
    )
    if not has_change:
        errors.append(
            "At least one field (name, fix_value, formula, currency, description) "
            "must be provided"
        )

    if errors:
        error_msg = "; ".join(errors)
        raise ToolError(f"Cannot update cost: {error_msg}. Check required fields.")

    # Build params with correct API field names
    normalized_cost_type = cost_type.upper()
    params: dict[str, Any] = {
        "id": cost_id,
        "costType": normalized_cost_type,
    }
    if name is not None:
        params["name"] = name.strip()
    if fix_value is not None:
        params["fixValue"] = fix_value
    if formula is not None:
        # Formula items are @Transient — stored separately via /formulacostitem.
        # Store for the update step to handle.
        params["_formula_update"] = formula.strip()
    if description is not None:
        params["additionalInformation"] = description.strip()

    return params


async def cost_update(
    cost_id: str | int,
    cost_type: str,
    name: str | None = None,
    fix_value: float | None = None,
    formula: str | None = None,
    currency: str | None = None,
    description: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Update a cost. Audited write operation.

    Args:
        cost_id: Cost ID to update.
        cost_type: "FIX" or "FORMULA".
        name: New name (optional).
        fix_value: New amount for FIX type (optional).
        formula: New formula for FORMULA type (optional).
        currency: New currency code, e.g. "USD" (optional).
        description: New description (optional).
        instance: Instance profile name (from instance_list).

    Returns:
        dict with id, name, cost_type, amount/formula, previous_state, audit_id.
    """
    # Pre-flight validation (no audit record for validation failures)
    validated_params = _validate_cost_update_params(
        cost_id, cost_type, name, fix_value, formula, currency, description
    )

    # Get authenticated user for audit
    try:
        user_email = get_current_user_email(instance=instance)
    except NotAuthenticatedError as e:
        raise ToolError(str(e))

    # Use single BPAClient connection for all operations
    try:
        async with BPAClient.for_instance(instance) as client:
            # Capture current state for rollback before creating audit record
            try:
                current_state = await client.get(
                    "/cost/{cost_id}",
                    path_params={"cost_id": cost_id},
                    resource_type="cost",
                    resource_id=cost_id,
                )
            except BPANotFoundError:
                raise ToolError(
                    f"Cost '{cost_id}' not found. "
                    "Verify the cost_id from 'cost_create_fixed' or "
                    "'cost_create_formula' response."
                )

            # Resolve currency code to UUID if a new currency was provided
            if currency is not None:
                currency_id = await _resolve_currency_id(client, currency)
                validated_params["currencyId"] = currency_id

            # Create audit record BEFORE API call (audit-before-write pattern)
            audit_logger = AuditLogger(db_path=resolve_db_path(instance))
            audit_id = await audit_logger.record_pending(
                user_email=user_email,
                operation_type="update",
                object_type="cost",
                object_id=str(cost_id),
                params={
                    "changes": {k: v for k, v in validated_params.items() if k != "id"},
                },
            )

            try:
                # Extract formula update (handled separately via /formulacostitem)
                formula_update = validated_params.pop("_formula_update", None)

                # Use typed endpoints (PUT /fixcost or /formulacost) instead
                # of generic PUT /cost, which only updates base fields and
                # silently ignores fixValue and other type-specific fields.
                normalized_type = validated_params.get(
                    "costType", current_state.get("costType", "")
                )
                if normalized_type == "FIX":
                    put_endpoint = "/fixcost"
                elif normalized_type == "FORMULA":
                    put_endpoint = "/formulacost"
                else:
                    put_endpoint = "/cost"

                # Merge user changes into current_state so unmodified
                # fields are preserved.
                full_params = dict(current_state)
                # Remove @Transient formulaCostItems from PUT payload
                full_params.pop("formulaCostItems", None)
                for key, value in validated_params.items():
                    full_params[key] = value

                await client.put(
                    put_endpoint,
                    json=full_params,
                    resource_type="cost",
                    resource_id=cost_id,
                )

                # Update formula items separately if formula changed
                if formula_update is not None:
                    existing_items = current_state.get("formulaCostItems", [])
                    if existing_items:
                        # Update existing first item
                        item = dict(existing_items[0])
                        item["infixFormula"] = formula_update
                        await client.put(
                            "/formulacostitem",
                            json=item,
                            resource_type="cost",
                        )
                    else:
                        # Create new item
                        await client.post(
                            "/formulacostitem",
                            json={
                                "formulaCostId": str(cost_id),
                                "infixFormula": formula_update,
                                "sortOrder": 0,
                            },
                            resource_type="cost",
                        )

                # PUT returns empty body — re-fetch updated state
                cost_data = await client.get(
                    "/cost/{cost_id}",
                    path_params={"cost_id": cost_id},
                    resource_type="cost",
                    resource_id=cost_id,
                )

                # Save rollback state (previous state for restore)
                await audit_logger.save_rollback_state(
                    audit_id=audit_id,
                    object_type="cost",
                    object_id=str(cost_id),
                    previous_state=current_state,
                )

                # Mark audit as success
                await audit_logger.mark_success(
                    audit_id,
                    result={
                        "cost_id": cost_data.get("id"),
                        "name": cost_data.get("name"),
                        "changes_applied": {
                            k: v for k, v in validated_params.items() if k != "id"
                        },
                    },
                )

                result = _transform_cost_response(cost_data)
                result["previous_state"] = _transform_cost_response(current_state)
                result["audit_id"] = audit_id
                return result

            except BPANotFoundError:
                # Mark audit as failed
                await audit_logger.mark_failed(audit_id, f"Cost '{cost_id}' not found")
                raise ToolError(
                    f"Cost '{cost_id}' not found. "
                    "Verify the cost_id from 'cost_create_fixed' or "
                    "'cost_create_formula' response."
                )
            except BPAClientError as e:
                # Mark audit as failed
                await audit_logger.mark_failed(audit_id, str(e))
                raise translate_error(e, resource_type="cost", resource_id=cost_id)

    except ToolError:
        raise
    except BPAClientError as e:
        raise translate_error(e, resource_type="cost", resource_id=cost_id)


def _validate_cost_delete_params(cost_id: str | int) -> None:
    """Validate cost_delete parameters (pre-flight).

    Raises ToolError if validation fails.

    Args:
        cost_id: Cost ID to delete (required).

    Raises:
        ToolError: If validation fails.
    """
    if not cost_id:
        raise ToolError(
            "Cannot delete cost: 'cost_id' is required. "
            "Cost IDs are returned from 'cost_create_fixed' or "
            "'cost_create_formula'."
        )


async def cost_delete(
    cost_id: str | int, instance: str | None = None
) -> dict[str, Any]:
    """Delete a cost. Audited write operation.

    Args:
        cost_id: Cost ID to delete.
        instance: Instance profile name (from instance_list).

    Returns:
        dict with deleted (bool), cost_id, deleted_cost, audit_id.
    """
    # Pre-flight validation (no audit record for validation failures)
    _validate_cost_delete_params(cost_id)

    # Get authenticated user for audit
    try:
        user_email = get_current_user_email(instance=instance)
    except NotAuthenticatedError as e:
        raise ToolError(str(e))

    # Use single BPAClient connection for all operations
    try:
        async with BPAClient.for_instance(instance) as client:
            # Capture current state for rollback before creating audit record
            try:
                current_state = await client.get(
                    "/cost/{cost_id}",
                    path_params={"cost_id": cost_id},
                    resource_type="cost",
                    resource_id=cost_id,
                )
            except BPANotFoundError:
                raise ToolError(
                    f"Cost '{cost_id}' not found. "
                    "Verify the cost_id from 'cost_create_fixed' or "
                    "'cost_create_formula' response."
                )

            # Create audit record BEFORE API call (audit-before-write pattern)
            audit_logger = AuditLogger(db_path=resolve_db_path(instance))
            audit_id = await audit_logger.record_pending(
                user_email=user_email,
                operation_type="delete",
                object_type="cost",
                object_id=str(cost_id),
                params={},
            )

            try:
                await client.delete(
                    "/cost/{cost_id}",
                    path_params={"cost_id": cost_id},
                    resource_type="cost",
                    resource_id=cost_id,
                )

                # Save rollback state (full object for recreation)
                await audit_logger.save_rollback_state(
                    audit_id=audit_id,
                    object_type="cost",
                    object_id=str(cost_id),
                    previous_state=current_state,
                )

                # Mark audit as success
                await audit_logger.mark_success(
                    audit_id,
                    result={
                        "deleted": True,
                        "cost_id": str(cost_id),
                    },
                )

                return {
                    "deleted": True,
                    "cost_id": str(cost_id),
                    "deleted_cost": _transform_cost_response(current_state),
                    "audit_id": audit_id,
                }

            except BPANotFoundError:
                # Mark audit as failed
                await audit_logger.mark_failed(audit_id, f"Cost '{cost_id}' not found")
                raise ToolError(
                    f"Cost '{cost_id}' not found. "
                    "Verify the cost_id from 'cost_create_fixed' or "
                    "'cost_create_formula' response."
                )
            except BPAClientError as e:
                # Mark audit as failed
                await audit_logger.mark_failed(audit_id, str(e))
                raise translate_error(e, resource_type="cost", resource_id=cost_id)

    except ToolError:
        raise
    except BPAClientError as e:
        raise translate_error(e, resource_type="cost", resource_id=cost_id)


def register_cost_tools(mcp: Any) -> None:
    """Register cost tools with the MCP server.

    Args:
        mcp: The FastMCP server instance.
    """
    from mcp_eregistrations_bpa.tools.annotations import DESTRUCTIVE, WRITE

    # Write operations (audit-before-write pattern)
    mcp.tool(annotations=WRITE)(cost_create_fixed)
    mcp.tool(annotations=WRITE)(cost_create_formula)
    mcp.tool(annotations=WRITE)(cost_update)
    mcp.tool(annotations=DESTRUCTIVE)(cost_delete)
