"""MCP tools for managing API assertions."""

import json
import re
import time

from mcp.types import TextContent, Tool

from maeris_mcp.maeris.client import MaerisClient

# ── response-analysis helpers ────────────────────────────────────────────────

_EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
_URL_RE = re.compile(r"^https?://")


def _detect_type(value) -> str:
    if value is None:
        return "null"
    if isinstance(value, bool):
        return "boolean"
    if isinstance(value, (int, float)):
        return "number"
    if isinstance(value, str):
        return "string"
    if isinstance(value, list):
        return "array"
    if isinstance(value, dict):
        return "object"
    return "unknown"


def _is_id_field(name: str) -> bool:
    n = name.lower()
    return n == "id" or n.endswith("_id") or n.endswith("id")


def _analyze_response(response_data, response_time_ms: float, response_size_bytes: int) -> list[dict]:
    """Inspect actual response data and return a list of assertion suggestions."""
    suggestions: list[dict] = []
    top_type = _detect_type(response_data)

    # 1. Structure type
    article = "an" if top_type[0] in "aeiou" else "a"
    suggestions.append(
        {
            "title": f"Response is always {article} {top_type}",
            "description": f"Verify the top-level response is always a {top_type}.",
            "assertionNature": "structural_assertion",
            "assertionType": "structure_type",
            "assertionValue": {"expectedType": top_type},
        }
    )

    if top_type == "array":
        items = [i for i in response_data if isinstance(i, dict)]

        # 2. Exact count
        if response_data:
            suggestions.append(
                {
                    "title": f"Response returns {len(response_data)} record(s)",
                    "description": f"Verify the response always contains exactly {len(response_data)} item(s) (based on current run).",
                    "assertionNature": "dataset_assertion",
                    "assertionType": "exact_count",
                    "assertionValue": {"operator": "equals", "value": len(response_data)},
                }
            )

        if items:
            sample = items[0]

            # 3. Type enforcement
            type_map = {k: _detect_type(v) for k, v in sample.items()}
            suggestions.append(
                {
                    "title": "All fields have the correct data type",
                    "description": "Verify each field's data type matches what was observed in the current response.",
                    "assertionNature": "structural_assertion",
                    "assertionType": "type_enforcement",
                    "assertionValue": type_map,
                }
            )

            # 4. Required keys (present in ALL items)
            common_keys = set(sample.keys())
            for item in items:
                common_keys &= set(item.keys())
            if common_keys:
                key_list = sorted(common_keys)
                preview = ", ".join(f"'{k}'" for k in key_list[:4])
                if len(key_list) > 4:
                    preview += f" (and {len(key_list) - 4} more)"
                suggestions.append(
                    {
                        "title": "All required fields are always present",
                        "description": f"Verify that {preview} always appear in every record.",
                        "assertionNature": "structural_assertion",
                        "assertionType": "required_keys",
                        "assertionValue": {k: True for k in common_keys},
                    }
                )

            # 5. Uniqueness for the first ID-like field
            for key in sample:
                if _is_id_field(key):
                    vals = [item.get(key) for item in items if item.get(key) is not None]
                    if vals and len(vals) == len({str(v) for v in vals}):
                        suggestions.append(
                            {
                                "title": f"'{key}' is unique across all records",
                                "description": f"Verify that no two records share the same '{key}'.",
                                "assertionNature": "uniqueness_assertion",
                                "assertionType": "unique_in_array",
                                "assertionValue": {"field": key, "scope": "global"},
                            }
                        )
                    break

            # 6. Per-field validations (capped at 5 suggestions)
            field_count = 0
            for key, val in sample.items():
                if field_count >= 5:
                    break
                if _is_id_field(key):
                    continue

                all_vals = [item.get(key) for item in items if item.get(key) is not None]
                val_type = _detect_type(val)

                if val_type == "string":
                    str_vals = [v for v in all_vals if isinstance(v, str) and v]
                    if not str_vals:
                        continue
                    if all(_EMAIL_RE.match(v) for v in str_vals):
                        suggestions.append(
                            {
                                "title": f"'{key}' is always a valid email address",
                                "description": f"Verify that '{key}' always contains a properly formatted email.",
                                "assertionNature": "value_domain_assertion",
                                "assertionType": "email",
                                "assertionValue": {"field": key, "pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"},
                            }
                        )
                        field_count += 1
                    elif all(_URL_RE.match(v) for v in str_vals):
                        suggestions.append(
                            {
                                "title": f"'{key}' is always a valid URL",
                                "description": f"Verify that '{key}' always starts with http:// or https://.",
                                "assertionNature": "value_domain_assertion",
                                "assertionType": "url",
                                "assertionValue": {"field": key, "pattern": "^https?://"},
                            }
                        )
                        field_count += 1
                    elif len(str_vals) >= 3:
                        distinct = sorted(set(str_vals))
                        if 2 <= len(distinct) <= 6:
                            suggestions.append(
                                {
                                    "title": f"'{key}' only contains known values",
                                    "description": f"Verify that '{key}' is always one of: {', '.join(distinct)}.",
                                    "assertionNature": "value_domain_assertion",
                                    "assertionType": "enum",
                                    "assertionValue": {"field": key, "allowedValues": distinct},
                                }
                            )
                            field_count += 1

                elif val_type == "number":
                    num_vals = [v for v in all_vals if isinstance(v, (int, float))]
                    if not num_vals:
                        continue
                    min_val = min(num_vals)
                    if min_val >= 0:
                        suggestions.append(
                            {
                                "title": f"'{key}' is always non-negative",
                                "description": f"Verify that '{key}' never has a negative value (observed min: {min_val}).",
                                "assertionNature": "value_domain_assertion",
                                "assertionType": "min_value",
                                "assertionValue": {"field": key, "min": min_val},
                            }
                        )
                        field_count += 1

    elif top_type == "object":
        # 2. Type enforcement
        type_map = {k: _detect_type(v) for k, v in response_data.items()}
        suggestions.append(
            {
                "title": "All fields have the correct data type",
                "description": "Verify each field's data type matches what was observed in the current response.",
                "assertionNature": "structural_assertion",
                "assertionType": "type_enforcement",
                "assertionValue": type_map,
            }
        )

        # 3. Required keys
        if response_data:
            key_list = sorted(response_data.keys())
            preview = ", ".join(f"'{k}'" for k in key_list[:4])
            if len(key_list) > 4:
                preview += f" (and {len(key_list) - 4} more)"
            suggestions.append(
                {
                    "title": "All required fields are always present",
                    "description": f"Verify that {preview} always appear in the response.",
                    "assertionNature": "structural_assertion",
                    "assertionType": "required_keys",
                    "assertionValue": {k: True for k in response_data.keys()},
                }
            )

        # 4. Field validations
        for key, val in response_data.items():
            if _is_id_field(key) or not isinstance(val, str) or not val:
                continue
            if _EMAIL_RE.match(val):
                suggestions.append(
                    {
                        "title": f"'{key}' is always a valid email address",
                        "description": f"Verify that '{key}' always contains a properly formatted email.",
                        "assertionNature": "value_domain_assertion",
                        "assertionType": "email",
                        "assertionValue": {"field": key, "pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"},
                    }
                )
            elif _URL_RE.match(val):
                suggestions.append(
                    {
                        "title": f"'{key}' is always a valid URL",
                        "description": f"Verify that '{key}' always starts with http:// or https://.",
                        "assertionNature": "value_domain_assertion",
                        "assertionType": "url",
                        "assertionValue": {"field": key, "pattern": "^https?://"},
                    }
                )

    # Performance: response time
    threshold_ms = max(int(round(response_time_ms * 2 / 100.0) * 100), 500)
    suggestions.append(
        {
            "title": f"API responds within {threshold_ms}ms",
            "description": f"Verify the API responds in under {threshold_ms}ms (observed: ~{round(response_time_ms)}ms on current run).",
            "assertionNature": "performance_assertion",
            "assertionType": "response_time",
            "assertionValue": {"operator": "<", "unit": "ms", "threshold": threshold_ms},
        }
    )

    # Performance: response size
    if response_size_bytes > 0:
        threshold_kb = max(int(response_size_bytes / 1024 * 2) + 1, 1)
        suggestions.append(
            {
                "title": f"Response size stays under {threshold_kb}kb",
                "description": f"Verify the response doesn't grow too large (observed: ~{round(response_size_bytes / 1024, 1)}kb).",
                "assertionNature": "performance_assertion",
                "assertionType": "response_size",
                "assertionValue": {"operator": "<", "unit": "kb", "threshold": threshold_kb},
            }
        )

    return suggestions


def _assertion_id(item: dict) -> str:
    for key in ["api_assertion_id", "assertion_id", "id", "_id"]:
        value = item.get(key)
        if value:
            return str(value)
    return ""


def _normalize_assertion(item: dict) -> dict:
    return {
        "id": _assertion_id(item),
        "apiId": item.get("api_id") or item.get("apiId"),
        "assertionNature": item.get("assertion_nature") or item.get("assertionNature"),
        "assertionType": item.get("assertion_type") or item.get("assertionType"),
        "assertionValue": item.get("assertion_value") or item.get("assertionValue"),
        "createdAt": item.get("created_at"),
    }


async def _find_api_by_name(client: MaerisClient, api_name: str) -> tuple[dict | None, str | None]:
    apis = await client.get_all_apis()
    matches = [a for a in apis if (a.get("name") or "").lower() == api_name.lower()]

    if not matches:
        return None, f"Error: no API found with name '{api_name}'"

    if len(matches) > 1:
        candidates = [
            {
                "id": m.get("api_id") or m.get("id"),
                "name": m.get("name"),
                "url": m.get("url"),
                "collectionId": m.get("collection_id") or m.get("collectionId"),
                "collectionName": m.get("collection_name") or m.get("collectionName"),
            }
            for m in matches
        ]
        return None, (
            f"Error: multiple APIs found with name '{api_name}'. "
            f"Matches: {json.dumps(candidates)}"
        )

    return matches[0], None


def add_assertion_tool() -> Tool:
    return Tool(
        name="add_assertion",
        description=(
            "Add an assertion to an API by API name. "
            "ALWAYS use the correct assertion_nature and assertion_value structure shown below.\n\n"
            "assertion_nature → assertion_type values:\n"
            "  'structural_assertion'   → structure_type, type_enforcement, required_keys, no_extra_keys, schema_fields\n"
            "  'value_domain_assertion' → min_length, max_length, min_value, max_value, pattern, email, url, enum, conditional_static_value_check, conditional_range_check, conditional_pattern_check\n"
            "  'uniqueness_assertion'   → unique_in_array, unique_per_group\n"
            "  'referential_assertion'  → foreign_key, parent_child, cross_reference, sequential_ids\n"
            "  'dataset_assertion'      → exact_count, unique_count, field_sum, field_average, field_min, field_max, field_median, field_percentile\n"
            "  'performance_assertion'  → response_time, response_size, throughput, error_rate\n\n"
            "assertion_value shapes:\n"
            "  structure_type:    {\"expectedType\": \"array\"|\"object\"|\"string\"|\"number\"|\"boolean\"|\"null\"}\n"
            "  type_enforcement:  {\"fieldName\": \"string\"|\"number\"|\"boolean\"|\"object\"|\"array\"|\"null\"|\"unknown\", ...}\n"
            "  required_keys:     {\"fieldName\": true|false, ...}\n"
            "  no_extra_keys:     {\"enabled\": true}\n"
            "  schema_fields:     {\"selectedFields\": [\"field1\", \"field2\"]}\n"
            "  min_length:        {\"field\": \"fieldName\", \"minLength\": N}\n"
            "  max_length:        {\"field\": \"fieldName\", \"maxLength\": N}\n"
            "  min_value:         {\"field\": \"fieldName\", \"min\": N}\n"
            "  max_value:         {\"field\": \"fieldName\", \"max\": N}\n"
            "  pattern:           {\"field\": \"fieldName\", \"pattern\": \"regex\"}\n"
            "  email:             {\"field\": \"fieldName\", \"pattern\": \"...\"}\n"
            "  url:               {\"field\": \"fieldName\", \"pattern\": \"...\"}\n"
            "  enum:              {\"field\": \"fieldName\", \"allowedValues\": [\"v1\", \"v2\"]}\n"
            "  conditional_static_value_check: {\"where\": {\"field\": \"f\", \"equals\": \"v\"}, \"assert\": [{\"field\": \"f\", \"rule\": \"equals\", \"value\": v}]}\n"
            "  conditional_range_check:        {\"where\": {\"field\": \"f\", \"equals\": \"v\"}, \"assert\": [{\"field\": \"f\", \"rule\": \"min\"|\"max\", \"value\": N}]}\n"
            "  conditional_pattern_check:      {\"where\": {\"field\": \"f\", \"equals\": \"v\"}, \"assert\": [{\"field\": \"f\", \"rule\": \"startsWith\"|\"endsWith\"|\"contains\"|\"regex\", \"value\": \"...\"}]}\n"
            "  unique_in_array:   {\"field\": \"fieldName\", \"scope\": \"global\"}\n"
            "  unique_per_group:  {\"field\": \"fieldName\", \"scope\": \"grouped\", \"groupBy\": \"categoryField\"}\n"
            "  foreign_key|parent_child|cross_reference: {\"field\": \"src\", \"referenceApiId\": \"id\", \"referenceField\": \"tgt\"}\n"
            "  sequential_ids:    {\"field\": \"idField\", \"mode\": \"strict\", \"startFrom\": 1, \"sortOrder\": \"asc\"|\"desc\"}\n"
            "  exact_count:       {\"operator\": \"equals\"|\"between\"|\">\"|\"<\"|\">\=\"|\"<=\", \"value\": N} (use min+max for between)\n"
            "  unique_count:      {\"field\": \"f\", \"operator\": \"...\", \"value\": N}\n"
            "  field_sum|field_average|field_min|field_max|field_median|field_percentile: {\"field\": \"f\", \"operator\": \"...\", \"value\": N}\n"
            "  response_time:     {\"operator\": \"<\", \"unit\": \"ms\"|\"s\", \"threshold\": N}\n"
            "  response_size:     {\"operator\": \"<\", \"unit\": \"b\"|\"kb\"|\"mb\", \"threshold\": N}\n"
            "  throughput:        {\"operator\": \">\", \"unit\": \"req/sec\"|\"req/min\", \"threshold\": N}\n"
            "  error_rate:        {\"operator\": \"<\", \"unit\": \"%\", \"threshold\": N}"
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "api_name": {"type": "string", "description": "Exact API name."},
                "assertion_nature": {
                    "type": "string",
                    "enum": ["structural_assertion", "value_domain_assertion", "uniqueness_assertion", "referential_assertion", "dataset_assertion", "performance_assertion"],
                    "description": "Assertion nature (see tool description for valid nature-type pairings).",
                },
                "assertion_type": {"type": "string", "description": "Assertion type (see tool description for valid types per nature)."},
                "assertion_value": {"type": "object", "description": "Assertion value object (see tool description for correct shape per type)."},
            },
            "required": ["api_name", "assertion_nature", "assertion_type", "assertion_value"],
        },
    )


def list_assertion_types_tool() -> Tool:
    return Tool(
        name="list_assertion_types",
        description="List all available assertion natures, their assertion types, and the required assertion_value structure for each. Use this before adding an assertion to understand what options are available.",
        inputSchema={"type": "object", "properties": {}, "required": []},
    )


def get_assertions_tool() -> Tool:
    return Tool(
        name="get_assertions",
        description="List all API assertions.",
        inputSchema={"type": "object", "properties": {}, "required": []},
    )


def get_assertions_for_api_tool() -> Tool:
    return Tool(
        name="get_assertions_for_api",
        description="List assertions for one API by API name.",
        inputSchema={
            "type": "object",
            "properties": {
                "api_name": {"type": "string", "description": "Exact API name."},
            },
            "required": ["api_name"],
        },
    )


def update_assertion_tool() -> Tool:
    return Tool(
        name="update_assertion",
        description="Update assertion_value for a specific assertion id.",
        inputSchema={
            "type": "object",
            "properties": {
                "api_assertion_id": {"type": "string", "description": "Assertion id to update."},
                "assertion_value": {"type": "object", "description": "New assertion value object."},
            },
            "required": ["api_assertion_id", "assertion_value"],
        },
    )


def delete_assertion_tool() -> Tool:
    return Tool(
        name="delete_assertion",
        description="Delete an assertion by assertion id.",
        inputSchema={
            "type": "object",
            "properties": {
                "api_assertion_id": {"type": "string", "description": "Assertion id to delete."},
            },
            "required": ["api_assertion_id"],
        },
    )


async def handle_add_assertion(arguments: dict) -> list[TextContent]:
    api_name = arguments.get("api_name")
    assertion_nature = arguments.get("assertion_nature")
    assertion_type = arguments.get("assertion_type")
    assertion_value = arguments.get("assertion_value")

    if not all([api_name, assertion_nature, assertion_type, assertion_value is not None]):
        return [
            TextContent(
                type="text",
                text="Error: api_name, assertion_nature, assertion_type, and assertion_value are required",
            )
        ]

    async with MaerisClient() as client:
        api, error = await _find_api_by_name(client, api_name)
        if error:
            return [TextContent(type="text", text=error)]
        api_id = api.get("api_id") or api.get("id")
        created = await client.add_assertion(
            str(api_id),
            str(assertion_nature),
            str(assertion_type),
            assertion_value,
        )

    return [TextContent(type="text", text=json.dumps(_normalize_assertion(created), indent=2))]


async def handle_list_assertion_types(arguments: dict) -> list[TextContent]:
    text = """
╔══════════════════════════════════════════════════════════════╗
║              ASSERTION TYPES — QUICK REFERENCE               ║
╚══════════════════════════════════════════════════════════════╝

Assertions are rules you set on an API to automatically verify
that its response is correct. Here's what you can check:

━━━ 1. SCHEMA & STRUCTURE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
These checks verify the overall shape of the API response.

  ▸ structure_type
      "Is the response the right type?"
      e.g. The response should always be a list of items (array),
           or a single record (object).

  ▸ type_enforcement
      "Are the fields the right data types?"
      e.g. 'age' should be a number, 'name' should be text,
           'is_active' should be true/false.

  ▸ required_keys
      "Which fields must always be present?"
      e.g. Every record must include 'id', 'name', and 'created_at'.

  ▸ no_extra_keys
      "Should the response contain only known fields?"
      e.g. No unexpected or extra fields should appear in the response.

  ▸ schema_fields
      "What is the full list of expected fields?"
      e.g. The response should only ever contain these fields: id, name, status.

━━━ 2. FIELD VALIDATION ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
These checks validate the actual values inside fields.

  ▸ min_length
      "Is this text long enough?"
      e.g. 'username' must be at least 5 characters long.

  ▸ max_length
      "Is this text not too long?"
      e.g. 'bio' must not exceed 200 characters.

  ▸ min_value
      "Is this number big enough?"
      e.g. 'price' must be at least 0.

  ▸ max_value
      "Is this number within the allowed range?"
      e.g. 'discount' must not exceed 100.

  ▸ pattern
      "Does this text match a specific format?"
      e.g. 'zip_code' must follow the format 12345.

  ▸ email
      "Is this a valid email address?"
      e.g. 'email' must look like user@example.com.

  ▸ url
      "Is this a valid web address?"
      e.g. 'website' must start with http:// or https://.

  ▸ enum
      "Is the value one of the allowed options?"
      e.g. 'status' must be one of: active, inactive, pending.

  ▸ conditional_static_value_check
      "If field X has value Y, then field Z must equal W."
      e.g. If 'status' is 'active', then 'is_verified' must be true.

  ▸ conditional_range_check
      "If field X has value Y, then field Z must be within a range."
      e.g. If 'type' is 'premium', then 'price' must be between 100 and 999.

  ▸ conditional_pattern_check
      "If field X has value Y, then field Z must match a pattern."
      e.g. If 'country' is 'US', then 'zip_code' must be 5 digits.

━━━ 3. UNIQUENESS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
These checks ensure no duplicate values exist.

  ▸ unique_in_array
      "Are all values in this field unique across the response?"
      e.g. No two records should have the same 'id'.

  ▸ unique_per_group
      "Are values unique within each group?"
      e.g. No two products in the same 'category' should have the same 'sku'.

━━━ 4. REFERENTIAL INTEGRITY ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
These checks validate relationships between different APIs.

  ▸ foreign_key
      "Does this field's value exist in another API?"
      e.g. 'user_id' must match an existing user in the Users API.

  ▸ parent_child
      "Is this record properly linked to its parent?"
      e.g. Every 'comment' must belong to an existing 'post'.

  ▸ cross_reference
      "Does this value appear in a related API's response?"
      e.g. 'category_id' must exist in the Categories API.

  ▸ sequential_ids
      "Are the IDs in order without gaps?"
      e.g. Records should be numbered 1, 2, 3... with no skips.

━━━ 5. DATASET ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
These checks validate totals and counts across the full response.

  ▸ exact_count
      "How many records should the response return?"
      e.g. The response must contain exactly 10 items.

  ▸ unique_count
      "How many distinct values should a field have?"
      e.g. There should be exactly 5 unique categories.

  ▸ field_sum
      "Should the total of a field meet a threshold?"
      e.g. The sum of all 'amounts' must be at least 1000.

  ▸ field_average
      "Should the average of a field be within a range?"
      e.g. The average 'rating' should be between 3.5 and 5.0.

  ▸ field_min
      "What is the minimum value allowed across the dataset?"
      e.g. The lowest 'price' in the list must be at least 0.

  ▸ field_max
      "What is the maximum value allowed across the dataset?"
      e.g. The highest 'score' must not exceed 100.

  ▸ field_median
      "What should the middle value of a field be?"
      e.g. The median 'response_time' should equal 50ms.

  ▸ field_percentile
      "What should the value at a given percentile be?"
      e.g. The 95th percentile of 'load_time' should be under 2 seconds.

━━━ 6. PERFORMANCE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
These checks validate how fast and reliable the API is.

  ▸ response_time
      "How quickly should the API respond?"
      e.g. The API must respond in under 1000ms.

  ▸ response_size
      "How large should the response be?"
      e.g. The response must be smaller than 100kb.

  ▸ throughput
      "How many requests should the API handle per second?"
      e.g. The API must handle at least 10 requests per second.

  ▸ error_rate
      "How often is the API allowed to fail?"
      e.g. The error rate must stay below 5%.

──────────────────────────────────────────────────────────────
Just tell me what you want to check and I'll pick the right
assertion type and set it up for you!
──────────────────────────────────────────────────────────────
"""
    return [TextContent(type="text", text=text.strip())]


async def handle_get_assertions(arguments: dict) -> list[TextContent]:
    async with MaerisClient() as client:
        assertions = await client.get_assertions()

    return [
        TextContent(
            type="text",
            text=json.dumps([_normalize_assertion(a) for a in assertions], indent=2),
        )
    ]


async def handle_get_assertions_for_api(arguments: dict) -> list[TextContent]:
    api_name = arguments.get("api_name")
    if not api_name:
        return [TextContent(type="text", text="Error: api_name is required")]

    async with MaerisClient() as client:
        api, error = await _find_api_by_name(client, api_name)
        if error:
            return [TextContent(type="text", text=error)]
        api_id = api.get("api_id") or api.get("id")
        assertions = await client.get_assertions_for_api(str(api_id))

    return [
        TextContent(
            type="text",
            text=json.dumps([_normalize_assertion(a) for a in assertions], indent=2),
        )
    ]


async def handle_update_assertion(arguments: dict) -> list[TextContent]:
    api_assertion_id = arguments.get("api_assertion_id")
    assertion_value = arguments.get("assertion_value")

    if not api_assertion_id or assertion_value is None:
        return [
            TextContent(
                type="text",
                text="Error: api_assertion_id and assertion_value are required",
            )
        ]

    async with MaerisClient() as client:
        updated = await client.update_assertion(str(api_assertion_id), assertion_value)

    return [TextContent(type="text", text=json.dumps(_normalize_assertion(updated), indent=2))]


async def handle_delete_assertion(arguments: dict) -> list[TextContent]:
    api_assertion_id = arguments.get("api_assertion_id")
    if not api_assertion_id:
        return [TextContent(type="text", text="Error: api_assertion_id is required")]

    async with MaerisClient() as client:
        await client.delete_assertion(str(api_assertion_id))

    result = {"deleted": True, "id": str(api_assertion_id)}
    return [TextContent(type="text", text=json.dumps(result, indent=2))]


def generate_assertions_tool() -> Tool:
    return Tool(
        name="generate_assertions",
        description=(
            "The most powerful assertion tool. Runs an API, inspects the actual response, "
            "and generates a complete, meaningful assertion suite specific to the real data — "
            "not generic boilerplate. Analyzes field types, patterns (email, URL), "
            "allowed values, numeric ranges, uniqueness, and performance.\n\n"
            "Workflow:\n"
            "1. Runs the specified API (using the current environment if set).\n"
            "2. Analyzes the actual JSON response shape and field values.\n"
            "3. Returns a numbered list of suggested assertions with a pendingDecision.\n"
            "4. ALWAYS present the full list to the user and ask which to save.\n"
            "5. Call add_assertion for each assertion the user selects, using the exact "
            "assertionNature, assertionType, and assertionValue from the suggestion."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "api_name": {"type": "string", "description": "Exact name of the API to run and analyze."},
                "env_name": {"type": "string", "description": "Optional environment name to use when running the API."},
            },
            "required": ["api_name"],
        },
    )


async def handle_generate_assertions(arguments: dict) -> list[TextContent]:
    api_name = arguments.get("api_name")
    env_name = arguments.get("env_name")

    if not api_name:
        return [TextContent(type="text", text="Error: api_name is required")]

    async with MaerisClient() as client:
        api, error = await _find_api_by_name(client, str(api_name))
        if error:
            return [TextContent(type="text", text=error)]
        api_id = api.get("api_id") or api.get("id")

        # Resolve environment
        env_id: str | None = None
        if env_name:
            envs = await client.get_environments()
            matched = [e for e in envs if (e.get("name") or "").lower() == str(env_name).lower()]
            if not matched:
                return [TextContent(type="text", text=f"Error: no environment found with name '{env_name}'")]
            env_id = str(matched[0].get("api_environment_id") or matched[0].get("id") or "")

        # Run the API and measure elapsed time
        t0 = time.time()
        try:
            run_result = await client.run_api(str(api_id), env_id=env_id or None)
        except Exception as exc:
            return [TextContent(type="text", text=f"Error running API '{api_name}': {exc}")]
        elapsed_ms = (time.time() - t0) * 1000

    # Extract the actual payload from the run result
    response_data = run_result
    for key in ("response", "data", "body", "result"):
        if isinstance(run_result, dict) and key in run_result:
            candidate = run_result[key]
            if isinstance(candidate, (dict, list)):
                response_data = candidate
                break

    response_size_bytes = len(json.dumps(response_data).encode("utf-8"))

    suggestions = _analyze_response(response_data, elapsed_ms, response_size_bytes)
    numbered = [{"index": i + 1, **s} for i, s in enumerate(suggestions)]

    # Build a brief preview of what the response looks like
    top_type = _detect_type(response_data)
    preview: dict = {"type": top_type}
    if top_type == "array":
        preview["itemCount"] = len(response_data)
        if response_data and isinstance(response_data[0], dict):
            preview["fields"] = list(response_data[0].keys())
    elif top_type == "object":
        preview["fields"] = list(response_data.keys())

    result = {
        "apiName": api_name,
        "apiId": str(api_id),
        "responsePreview": preview,
        "generatedAssertions": numbered,
        "pendingDecision": {
            "message": (
                f"I analyzed the live response from '{api_name}' and found {len(numbered)} assertion suggestion(s). "
                "Review the list above and tell me which ones to save — "
                "e.g. 'save all', 'save 1, 3, 5', or 'skip 2 and 4'."
            ),
            "action": (
                "For every assertion the user approves, call add_assertion with "
                "api_name=<apiName>, assertion_nature=<assertionNature>, "
                "assertion_type=<assertionType>, assertion_value=<assertionValue> "
                "taken directly from the corresponding suggestion object."
            ),
        },
    }
    return [TextContent(type="text", text=json.dumps(result, indent=2))]
