#!/usr/bin/env python3
"""
Trello Webhook Receiver - Harvey's real-time Trello sync
Listens on port 8766 for Trello webhook events.
Logs events to memory/trello-events.jsonl and triggers trello-sync.py.
"""
import http.server
import json
import os
import subprocess
import sys
import threading
from datetime import datetime, timezone

PORT = 8767
WORKSPACE = os.path.expanduser('~/.openclaw/workspace')
EVENTS_FILE = os.path.join(WORKSPACE, 'memory', 'trello-events.jsonl')
SYNC_SCRIPT = os.path.join(WORKSPACE, 'scripts', 'trello-sync.py')
LOG_FILE = '/tmp/openclaw/trello-webhook.log'


def log(msg):
    ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    line = f"[{ts}] {msg}"
    print(line, flush=True)
    try:
        os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
        with open(LOG_FILE, 'a') as f:
            f.write(line + '\n')
    except Exception:
        pass


def append_event(event_data, raw_body):
    """Write a compact summary of the event to the JSONL file."""
    try:
        action = event_data.get('action', {})
        action_type = action.get('type', 'unknown')
        card = action.get('data', {}).get('card', {})
        board = action.get('data', {}).get('board', {})
        list_after = action.get('data', {}).get('listAfter', {})
        list_before = action.get('data', {}).get('listBefore', {})
        member = action.get('memberCreator', {})

        summary = {
            'ts': datetime.now(timezone.utc).isoformat(),
            'type': action_type,
            'card': card.get('name', ''),
            'card_id': card.get('id', ''),
            'board': board.get('name', ''),
            'by': member.get('fullName', member.get('username', '')),
        }

        if list_after:
            summary['list_to'] = list_after.get('name', '')
        if list_before:
            summary['list_from'] = list_before.get('name', '')

        # For updateCard actions, capture what changed
        if action_type == 'updateCard':
            old = action.get('data', {}).get('old', {})
            if old:
                summary['changed'] = list(old.keys())

        os.makedirs(os.path.dirname(EVENTS_FILE), exist_ok=True)
        with open(EVENTS_FILE, 'a') as f:
            f.write(json.dumps(summary) + '\n')

        log(f"Event logged: {action_type} — {card.get('name', '?')}")
    except Exception as e:
        log(f"Error logging event: {e}")


def run_sync_async():
    """Run trello-sync.py in background (don't block webhook response)."""
    def _run():
        try:
            result = subprocess.run(
                [sys.executable, SYNC_SCRIPT],
                capture_output=True, text=True, timeout=60
            )
            if result.returncode == 0:
                log("trello-sync.py completed successfully")
            else:
                log(f"trello-sync.py error: {result.stderr[:200]}")
        except subprocess.TimeoutExpired:
            log("trello-sync.py timed out after 60s")
        except Exception as e:
            log(f"Failed to run trello-sync.py: {e}")

    t = threading.Thread(target=_run, daemon=True)
    t.start()


class ReusableTCPServer(http.server.HTTPServer):
    allow_reuse_address = True


class TrelloWebhookHandler(http.server.BaseHTTPRequestHandler):

    def log_message(self, format, *args):
        # Suppress default access log noise; we have our own logging
        pass

    def handle_one_request(self):
        """Override to support HEAD method (not in BaseHTTP's default allow list)."""
        try:
            self.raw_requestline = self.rfile.readline(65537)
            if len(self.raw_requestline) > 65536:
                self.requestline = ''
                self.request_version = ''
                self.command = ''
                self.send_error(http.server.HTTPStatus.REQUEST_URI_TOO_LONG)
                return
            if not self.raw_requestline:
                self.close_connection = True
                return
            if not self.parse_request():
                return
            mname = 'do_' + self.command
            if not hasattr(self, mname):
                self.send_error(
                    http.server.HTTPStatus.NOT_IMPLEMENTED,
                    "Unsupported method (%r)" % self.command)
                return
            method = getattr(self, mname)
            method()
            self.wfile.flush()
        except TimeoutError as e:
            self.log_error("Request timed out: %r", e)
            self.close_connection = True

    def do_HEAD(self):
        """Trello sends HEAD to verify the webhook endpoint exists."""
        log(f"HEAD {self.path} — webhook verification")
        self.send_response(200)
        self.send_header('Content-Type', 'text/plain')
        self.send_header('Content-Length', '0')
        self.end_headers()

    def do_GET(self):
        """Health check endpoint."""
        if self.path == '/health':
            self.send_response(200)
            self.send_header('Content-Type', 'text/plain')
            self.end_headers()
            self.wfile.write(b'Harvey Trello Webhook Receiver OK')
        else:
            self.send_response(404)
            self.end_headers()

    def do_POST(self):
        """Handle incoming Trello webhook events."""
        if self.path != '/trello-webhook':
            self.send_response(404)
            self.end_headers()
            return

        try:
            content_length = int(self.headers.get('Content-Length', 0))
            raw_body = self.rfile.read(content_length) if content_length > 0 else b''

            # Respond 200 immediately (Trello expects quick response)
            self.send_response(200)
            self.send_header('Content-Type', 'text/plain')
            self.send_header('Content-Length', '2')
            self.end_headers()
            self.wfile.write(b'OK')

            # Parse and process asynchronously
            if raw_body:
                try:
                    event_data = json.loads(raw_body)
                    append_event(event_data, raw_body)
                    run_sync_async()
                except json.JSONDecodeError as e:
                    log(f"Invalid JSON in webhook body: {e}")
            else:
                log("POST with empty body (ping?)")

        except Exception as e:
            log(f"Error handling POST: {e}")
            try:
                self.send_response(500)
                self.end_headers()
            except Exception:
                pass


def main():
    os.makedirs('/tmp/openclaw', exist_ok=True)
    log(f"Starting Trello webhook receiver on port {PORT}")
    log(f"Events file: {EVENTS_FILE}")
    log(f"Sync script: {SYNC_SCRIPT}")

    server = ReusableTCPServer(('0.0.0.0', PORT), TrelloWebhookHandler)
    log(f"Listening on 0.0.0.0:{PORT}")

    try:
        server.serve_forever()
    except KeyboardInterrupt:
        log("Shutting down webhook receiver")
        server.shutdown()


if __name__ == '__main__':
    main()
