#!/usr/bin/env python3
"""
Obsidian Vault Reindex Endpoint
Lightweight HTTP server that triggers an incremental Obsidian vault reindex.

Listens on 0.0.0.0:18810 (configurable via PORT env var).
Called by n8n webhooks or systemd timers.

Endpoints:
  POST /reindex       -> trigger incremental reindex, returns JSON stats
  GET  /reindex/status -> check last index state
  GET  /healthz        -> returns ok
"""

import http.server
import json
import os
import subprocess
import sys
import threading
from pathlib import Path

PORT = int(os.environ.get("PORT", 18810))

REINDEX_SCRIPT = str(
    Path.home()
    / ".hermes/skills/note-taking/rag-search/scripts/reindex_obsidian.sh"
)
STATE_FILE = (
    Path.home() / ".hermes/data/rag-search/obsidian_index_state.json"
)

# Lock to prevent concurrent reindexing
_reindex_lock = threading.Lock()


def run_reindex() -> dict:
    """Run the incremental reindex script. Returns stats dict."""
    if not _reindex_lock.acquire(blocking=False):
        return {"error": "reindex already in progress", "status": "locked"}

    try:
        result = subprocess.run(
            [REINDEX_SCRIPT],
            capture_output=True,
            text=True,
            timeout=600,  # 10 min max for full reindex
        )
        if result.returncode != 0:
            return {
                "error": "reindex failed",
                "exit_code": result.returncode,
                "stderr": result.stderr.strip()[:500],
            }
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
            return {
                "error": "invalid json output",
                "stdout": result.stdout.strip()[:500],
            }
    except subprocess.TimeoutExpired:
        return {"error": "reindex timed out (600s)"}
    except Exception as e:
        return {"error": str(e)}
    finally:
        _reindex_lock.release()


def get_status() -> dict:
    """Read the last index state file."""
    if not STATE_FILE.exists():
        return {"indexed": False, "message": "no state file"}
    try:
        return json.loads(STATE_FILE.read_text())
    except (json.JSONDecodeError, IOError) as e:
        return {"error": str(e)}


class ReindexHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        path = self.path.rstrip("/")
        if path == "/healthz":
            self._json_response({"status": "ok"})
        elif path == "/reindex/status":
            self._json_response(get_status())
        else:
            self._json_response({"error": "not found"}, status=404)

    def do_POST(self):
        path = self.path.rstrip("/")
        if path == "/reindex":
            # Run in background thread so we can respond
            result = run_reindex()
            status = 200 if "error" not in result else 500
            self._json_response(result, status=status)
        else:
            self._json_response({"error": "not found"}, status=404)

    def _json_response(self, data, status=200):
        body = json.dumps(data, indent=2).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, format, *args):
        # Minimal logging
        pass


def main():
    server = http.server.HTTPServer(("0.0.0.0", PORT), ReindexHandler)
    print(f"obsidian-reindex-server listening on 0.0.0.0:{PORT}", flush=True)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    server.server_close()


if __name__ == "__main__":
    main()
