#!/usr/bin/env python3
"""
Out-of-Band Verification for Sensitive Actions

Usage:
    python3 verify-action.py --action "description" --source telegram|imessage|email|webchat
    python3 verify-action.py --action "description" --source telegram --wait

Sends verification request to alternate channel, optionally waits for response.
"""

import argparse
import json
import subprocess
import sys
import time
from datetime import datetime, timezone
from pathlib import Path

# Dustin's contact info
DUSTIN_IMESSAGE = "+639772725701"
DUSTIN_IMESSAGE_CHAT_ID = "3"  # From imsg chats
DUSTIN_TELEGRAM = "1193456137"

VERIFICATION_LOG = Path.home() / ".openclaw/workspace/security/verification-log.json"
TIMEOUT_SECONDS = 600  # 10 minutes
POLL_INTERVAL = 5  # Check every 5 seconds

CONFIRM_WORDS = ["yes", "confirmed", "approved", "do it", "go ahead", "confirm", "y", "ok", "okay"]
REJECT_WORDS = ["no", "stop", "cancel", "deny", "reject", "n", "nope"]


def send_imessage(message: str) -> bool:
    """Send verification via iMessage"""
    try:
        result = subprocess.run(
            ["imsg", "send", "--to", DUSTIN_IMESSAGE, "--text", message],
            capture_output=True,
            text=True,
            timeout=30
        )
        return result.returncode == 0 or "sent" in result.stdout.lower()
    except Exception as e:
        print(f"iMessage send failed: {e}", file=sys.stderr)
        return False


def send_telegram(message: str) -> bool:
    """Send verification via Telegram (private bot)"""
    try:
        result = subprocess.run(
            ["openclaw", "message", "send", 
             "--channel", "telegram",
             "--account-id", "private",
             "--target", DUSTIN_TELEGRAM,
             "--message", message],
            capture_output=True,
            text=True,
            timeout=30
        )
        return result.returncode == 0
    except Exception as e:
        print(f"Telegram send failed: {e}", file=sys.stderr)
        return False


def get_latest_imessage_reply(since_timestamp: str) -> tuple[str, str] | None:
    """Get the latest reply from Dustin via iMessage after a given timestamp"""
    try:
        result = subprocess.run(
            ["imsg", "history", "--chat-id", DUSTIN_IMESSAGE_CHAT_ID, "--limit", "5", "--json"],
            capture_output=True,
            text=True,
            timeout=15
        )
        if result.returncode != 0:
            return None
        
        for line in result.stdout.strip().split('\n'):
            if not line:
                continue
            try:
                msg = json.loads(line)
                # is_from_me=false means it's FROM Dustin
                if not msg.get("is_from_me", True):
                    msg_time = msg.get("created_at", "")
                    if msg_time > since_timestamp:
                        return (msg.get("text", "").strip().lower(), msg_time)
            except json.JSONDecodeError:
                continue
        return None
    except Exception as e:
        print(f"Failed to check iMessage: {e}", file=sys.stderr)
        return None


def log_verification(action: str, source: str, verify_channel: str, result: str, 
                     response_time: float = None, executed: bool = False):
    """Log verification attempt"""
    log_entry = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "action": action,
        "sourceChannel": source,
        "verifyChannel": verify_channel,
        "result": result,
        "responseTime": response_time,
        "executed": executed
    }
    
    VERIFICATION_LOG.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        if VERIFICATION_LOG.exists():
            logs = json.loads(VERIFICATION_LOG.read_text())
        else:
            logs = []
        logs.append(log_entry)
        VERIFICATION_LOG.write_text(json.dumps(logs, indent=2))
    except Exception as e:
        print(f"Failed to log verification: {e}", file=sys.stderr)


def format_verification_message(action: str, source: str) -> str:
    return f"""🔐 VERIFICATION REQUEST

Action: {action}
Source: {source}

Reply YES to confirm or NO to reject.
(Auto-rejects in 10 minutes if no response)"""


def wait_for_imessage_response(since_timestamp: str, timeout: int = TIMEOUT_SECONDS) -> tuple[str, float]:
    """Poll for iMessage response. Returns (result, response_time_seconds)"""
    start = time.time()
    
    while time.time() - start < timeout:
        reply = get_latest_imessage_reply(since_timestamp)
        if reply:
            text, _ = reply
            elapsed = time.time() - start
            
            if any(word in text for word in CONFIRM_WORDS):
                return ("confirmed", elapsed)
            elif any(word in text for word in REJECT_WORDS):
                return ("rejected", elapsed)
            # If reply doesn't match, keep waiting (might be unrelated message)
        
        time.sleep(POLL_INTERVAL)
    
    return ("timeout", timeout)


def main():
    parser = argparse.ArgumentParser(description="Out-of-band verification for sensitive actions")
    parser.add_argument("--action", required=True, help="Description of the action to verify")
    parser.add_argument("--source", required=True, choices=["telegram", "imessage", "email", "webchat"],
                       help="Channel where the request originated")
    parser.add_argument("--wait", action="store_true", help="Wait for response (polling mode)")
    parser.add_argument("--timeout", type=int, default=TIMEOUT_SECONDS, help="Timeout in seconds")
    parser.add_argument("--dry-run", action="store_true", help="Show what would be sent without sending")
    
    args = parser.parse_args()
    
    # Determine alternate channel
    if args.source == "imessage":
        verify_channel = "telegram"
        send_func = send_telegram
        can_poll = False  # TODO: implement Telegram polling
    else:
        verify_channel = "imessage"
        send_func = send_imessage
        can_poll = True
    
    message = format_verification_message(args.action, args.source)
    
    if args.dry_run:
        print(f"Would send to {verify_channel}:")
        print(message)
        return 0
    
    # Record timestamp before sending
    send_time = datetime.now(timezone.utc).isoformat()
    
    print(f"Sending verification request via {verify_channel}...")
    
    if not send_func(message):
        print(f"❌ Failed to send verification via {verify_channel}")
        log_verification(args.action, args.source, verify_channel, "send_failed")
        return 1
    
    print(f"✅ Verification request sent via {verify_channel}")
    
    if args.wait and can_poll:
        print(f"⏳ Waiting for response (timeout: {args.timeout}s)...")
        result, response_time = wait_for_imessage_response(send_time, args.timeout)
        
        if result == "confirmed":
            print(f"✅ CONFIRMED after {response_time:.1f}s")
            log_verification(args.action, args.source, verify_channel, "confirmed", response_time, True)
            return 0
        elif result == "rejected":
            print(f"❌ REJECTED after {response_time:.1f}s")
            log_verification(args.action, args.source, verify_channel, "rejected", response_time, False)
            return 2
        else:
            print(f"⏰ TIMEOUT after {args.timeout}s — action NOT executed")
            log_verification(args.action, args.source, verify_channel, "timeout", args.timeout, False)
            return 3
    else:
        print(f"⏳ Manual verification required — check {verify_channel}")
        log_verification(args.action, args.source, verify_channel, "pending")
        return 0


if __name__ == "__main__":
    sys.exit(main())

# TONY-APPROVED: 2026-03-01 | sha:f543b126
