"""
03_monitor_close_audit.py

PURPOSE
-------
Read live_signal_monitor_v2.py source and identify every code path that can
close a USDJPY position. Then verify each path matches the documented exit
rules from v5.1 Section 15.

DOCUMENTED USDJPY EXITS (v5.1):
  - TP at 0.70%
  - SL at 0.40%
  - 24h max hold
  - Z-EXIT: OFF (not used)

If the source contains any other close path firing on USDJPY — z-exit logic
not gated by pair, watchdog auto-close, margin auto-close, etc. — we want
to know.

INPUTS
------
  - Path to live_signal_monitor_v2.py

USAGE
-----
  python 03_monitor_close_audit.py path\\to\\live_signal_monitor_v2.py

OUTPUT
------
  - All close-related code blocks with line numbers
  - Pair gating analysis (does each close path check which pair it's running on?)
  - Verdict on whether USDJPY exit logic matches doc spec
"""

import re
import sys
from pathlib import Path


# Patterns that indicate position-close logic
CLOSE_PATTERNS = [
    (r"\.close\s*\(",              "method.close() call"),
    (r"close_position",            "close_position function/method"),
    (r"order_send.*TRADE_ACTION_DEAL", "MT5 order_send to close"),
    (r"position_close",            "MT5 position_close"),
    (r"force.{0,5}close",          "forced close"),
    (r"emergency.{0,5}close",      "emergency close"),
    (r"manual.{0,5}close",         "manual close"),
    (r"z.?exit",                   "z-exit logic"),
    (r"hold.{0,5}time.{0,5}exp",   "hold time expiry"),
    (r"max.{0,5}hold",             "max hold check"),
    (r"24.{0,5}h",                 "24-hour reference"),
    (r"52.{0,5}h",                 "52-hour reference"),
    (r"orphan",                    "orphan position handling"),
    (r"watchdog",                  "watchdog logic"),
]


def find_close_blocks(source_path):
    """Find all lines that match close patterns."""
    if not Path(source_path).exists():
        print(f"ERROR: source file not found: {source_path}")
        sys.exit(1)

    with open(source_path, encoding="utf-8") as f:
        lines = f.readlines()

    print(f"Loaded {len(lines)} lines from {source_path}\n")

    matches = []
    for line_num, line in enumerate(lines, start=1):
        line_stripped = line.rstrip()
        # Skip comments and empty lines
        if not line_stripped or line_stripped.lstrip().startswith("#"):
            continue
        for pattern, description in CLOSE_PATTERNS:
            if re.search(pattern, line_stripped, re.IGNORECASE):
                matches.append((line_num, description, line_stripped))
                break  # only flag once per line

    return lines, matches


def show_context(lines, line_num, before=5, after=10):
    """Print context around a line."""
    start = max(0, line_num - before - 1)
    end = min(len(lines), line_num + after)
    for i in range(start, end):
        marker = ">>>" if i + 1 == line_num else "   "
        print(f"  {marker} {i+1:5d}  {lines[i].rstrip()}")


def check_pair_gating(lines, line_num, window=20):
    """
    Check if a close-related code block is gated by pair.
    Looks for 'EURUSD'/'USDJPY' or 'pair ==' references in surrounding lines.
    """
    start = max(0, line_num - window)
    end = min(len(lines), line_num + 5)
    context = "".join(lines[start:end])

    has_eu = "EURUSD" in context
    has_uj = "USDJPY" in context
    has_pair_check = bool(re.search(r"pair\s*==|symbol\s*==|self\.pair", context))

    if has_pair_check:
        if has_eu and has_uj:
            return "DUAL — references both pairs"
        elif has_eu:
            return "EURUSD-gated only"
        elif has_uj:
            return "USDJPY-gated only"
        else:
            return "UNCLEAR — has pair check but neither EU/UJ in context"
    else:
        if has_eu or has_uj:
            return "MENTIONS pair but no explicit gate"
        else:
            return "UNGATED — fires for any pair"


def main():
    if len(sys.argv) < 2:
        print(__doc__)
        sys.exit(1)

    source_path = sys.argv[1]
    lines, matches = find_close_blocks(source_path)

    if not matches:
        print("No close-related code patterns found.")
        return

    print("=" * 70)
    print(f"FOUND {len(matches)} CLOSE-RELATED LINES")
    print("=" * 70)

    # Group by description for summary
    by_kind = {}
    for line_num, kind, content in matches:
        by_kind.setdefault(kind, []).append((line_num, content))

    print("\nSUMMARY by category:")
    for kind, items in by_kind.items():
        print(f"  {kind:<35}  {len(items)} occurrences")

    # Detailed view
    print("\n" + "=" * 70)
    print("DETAILED VIEW (each match with context + pair gating analysis)")
    print("=" * 70)

    for line_num, kind, content in matches:
        gating = check_pair_gating(lines, line_num)
        print(f"\n[Line {line_num}] {kind}  ({gating})")
        show_context(lines, line_num, before=3, after=5)

    # Verdict
    print("\n" + "=" * 70)
    print("AUDIT VERDICT")
    print("=" * 70)

    z_exit_lines = [m for m in matches if "z-exit" in m[1].lower()]
    if z_exit_lines:
        print(f"\nFound {len(z_exit_lines)} z-exit code reference(s).")
        print("Per v5.1 Section 15, USDJPY should have NO z-exit logic.")
        print("Verify each z-exit block is gated to EURUSD only.")
        for line_num, kind, content in z_exit_lines:
            gating = check_pair_gating(lines, line_num)
            mark = "✓" if "EURUSD-gated" in gating else "⚠"
            print(f"  {mark} Line {line_num}: {gating}")

    print("""
NEXT STEPS:
  1. For any line marked UNGATED that's a close path, check whether it fires
     during USDJPY trades.
  2. For any z-exit code not gated to EURUSD, that's the most likely cause
     of the unexplained early closes.
  3. Cross-reference with 02_close_forensics.py output: the closure time of
     ticket 1621018719 should map to one of these code paths.
""")


if __name__ == "__main__":
    main()
