"""Tool: run_apex -- execute anonymous Apex with savepoint safety."""

import json
from typing import Any, Dict

from ..sfdc.cli import resolve_org, run_apex as sf_run_apex, SfCliError
from ..sfdc.apex_templates import wrap_with_savepoint

TOOL_NAME = "run_apex"

TOOL_DEFINITION = {
    "name": TOOL_NAME,
    "description": (
        "Execute anonymous Apex code on a Salesforce org. "
        "Use ONLY when the user explicitly asks to run Apex code, execute specific Apex "
        "statements, or when the request cannot be fulfilled by list_orchestration_templates "
        "or fetch_orchestration_templates. "
        "Do NOT use this tool for orchestration template operations -- use "
        "list_orchestration_templates (to show/list) or fetch_orchestration_templates "
        "(to get/download) instead. "
        "By default, the code runs inside a Savepoint and is rolled back after execution, "
        "making it safe for read-only operations. "
        "Set allow_dml to true ONLY when the user explicitly confirms they want to persist "
        "data modifications (insert, update, delete, upsert). "
        "Output is captured from System.debug statements in the execution log. "
        "Org is auto-resolved from session or workspace default if not specified."
    ),
    "inputSchema": {
        "type": "object",
        "properties": {
            "apex_code": {
                "type": "string",
                "description": (
                    "The anonymous Apex code to execute. Do NOT include savepoint/rollback logic -- "
                    "that is handled automatically based on the allow_dml flag."
                ),
            },
            "org_alias": {
                "type": "string",
                "description": (
                    "Salesforce org alias or partial hint (e.g. 'itxdevpro', 'devpro', 'sit p3'). "
                    "Partial names are fuzzy-matched against authenticated orgs. "
                    "OMIT this parameter if the user did not mention a specific org -- "
                    "the tool will automatically use the session org or workspace default."
                ),
            },
            "allow_dml": {
                "type": "boolean",
                "description": (
                    "If false (default), wraps the code in a Savepoint and rolls back all DML after execution. "
                    "Set to true ONLY when the user explicitly wants to persist data changes. "
                    "IMPORTANT: Always confirm with the user before setting this to true."
                ),
                "default": False,
            },
            "timeout_seconds": {
                "type": "integer",
                "description": "Maximum seconds to wait for execution. Default: 120.",
                "default": 120,
            },
        },
        "required": ["apex_code"],
    },
}


async def execute(arguments: Dict[str, Any]) -> str:
    """Execute the run_apex tool."""
    apex_code = arguments.get("apex_code", "")
    allow_dml = arguments.get("allow_dml", False)
    timeout = arguments.get("timeout_seconds", 120)

    if not apex_code.strip():
        return json.dumps({
            "success": False,
            "error": "apex_code is required and cannot be empty.",
        }, indent=2)

    org_resolution = resolve_org(arguments.get("org_alias") or None)

    if "error" in org_resolution:
        return json.dumps({
            "success": False,
            "error": org_resolution["error"],
            "candidates": org_resolution.get("candidates", []),
        }, indent=2)

    org_alias = org_resolution["org_alias"]
    org_source = org_resolution["org_source"]

    wrapped_code = wrap_with_savepoint(apex_code, allow_dml=allow_dml)

    try:
        result = sf_run_apex(wrapped_code, org_alias=org_alias, timeout_seconds=timeout)
    except SfCliError as e:
        return json.dumps({
            "success": False,
            "error": str(e),
            "org": org_alias,
            "org_source": org_source,
            "stderr": e.stderr[:1000] if e.stderr else "",
        }, indent=2)

    tagged_output = {}
    for line in result.log.splitlines():
        for tag in ["###ERROR###", "###RESULT###", "###DATA###", "###CHUNK_"]:
            if tag in line:
                idx = line.index(tag) + len(tag)
                tagged_output.setdefault(tag.strip("#"), []).append(line[idx:])

    return json.dumps({
        "success": True,
        "org": org_alias,
        "org_source": org_source,
        "allow_dml": allow_dml,
        "log_lines": len(result.log.splitlines()),
        "tagged_output": tagged_output,
        "log_preview": result.log[:3000] if len(result.log) > 3000 else result.log,
    }, indent=2)
