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

import json
from typing import Any, Dict

from ..sfdc.cli import get_default_org, run_apex as sf_run_apex, list_orgs, 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 via the sf CLI. "
        "By default, the code runs inside a Savepoint and is rolled back after execution, "
        "making it safe for read-only operations (queries, inspections, CloudSense API calls). "
        "Set allow_dml to true ONLY when the user explicitly confirms they want to perform "
        "data modifications (insert, update, delete, upsert). "
        "Output is captured from System.debug statements in the execution log. "
        "For structured output, use System.debug(LoggingLevel.ERROR, '###TAG###' + data) "
        "and the results will be extracted."
    ),
    "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 (e.g. 'itxdevpro', 'sit p3'). "
                    "DO NOT ask the user for this -- if they don't mention an org, "
                    "simply omit this parameter and the tool will automatically use "
                    "the workspace's default target-org from Salesforce CLI."
                ),
            },
            "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", "")
    org_alias = arguments.get("org_alias") or None
    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)

    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:
        error_msg = str(e)
        response: dict = {
            "success": False,
            "error": error_msg,
            "stderr": e.stderr[:1000] if e.stderr else "",
        }
        if "No default" in error_msg or "target-org" in error_msg or not org_alias:
            available = list_orgs()
            if available:
                response["available_orgs"] = available
                response["hint"] = (
                    "No default org is configured. Use one of the available orgs above "
                    "by passing its alias as org_alias, or set a default with: "
                    "sf config set target-org <alias>"
                )
            else:
                response["hint"] = (
                    "No authenticated orgs found. Run 'sf org login web' to authorize an org."
                )
        return json.dumps(response, 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:])

    resolved_org = org_alias or get_default_org() or "(sf CLI default)"

    return json.dumps({
        "success": True,
        "org": resolved_org,
        "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)
