"""
Shared EA (Expert Advisor) file-based IPC communication.

Usage::

    from bridge.ea_ipc import send_ea_command

    result = send_ea_command(
        cmd={"action": "ping"},
        cmd_file=MT5_COMMON / "to_ea" / "mt5_cmd.json",
        rsp_file=MT5_COMMON / "from_ea" / "mt5_rsp.json",
        timeout=15.0,
    )
"""

from __future__ import annotations

import json
import time
from pathlib import Path
from typing import Any


def send_ea_command(
    cmd: dict[str, Any],
    cmd_file: str | Path,
    rsp_file: str | Path,
    timeout: float = 15.0,
    poll_interval: float = 0.2,
) -> dict[str, Any] | None:
    """Send a command to an EA via file-based IPC and wait for a response.

    1. Delete any stale response file
    2. Write the command file (JSON)
    3. Poll for response file to appear
    4. Parse and return JSON response, or None on timeout

    Timing: the delete-then-write gap is a TOCTOU window (the EA could write a
    response between steps 1 and 2). This is inherent to file-based IPC.
    For real-time trading, prefer socket- or pipe-based communication.
    """
    cmd_p = Path(cmd_file)
    rsp_p = Path(rsp_file)

    # Ensure parent directories exist
    cmd_p.parent.mkdir(parents=True, exist_ok=True)
    rsp_p.parent.mkdir(parents=True, exist_ok=True)

    # Step 1: clear stale response
    try:
        if rsp_p.exists():
            rsp_p.unlink()
    except OSError:
        pass

    # Step 2: write command
    cmd_p.write_text(json.dumps(cmd, ensure_ascii=False), encoding="utf-8")

    # Step 3: poll for response
    deadline = time.time() + timeout
    while time.time() < deadline:
        if rsp_p.exists():
            try:
                return json.loads(rsp_p.read_text(encoding="utf-8"))
            except (json.JSONDecodeError, OSError):
                # Response still being written — wait and retry
                pass
        time.sleep(poll_interval)

    return None
