#!/usr/bin/env python3
"""
Watch for code changes and restart a target container.

This script watches one or more directories (set via the `WATCH_PATHS`
environment variable, comma-separated) and restarts the container named by
`TARGET_CONTAINER` (defaults to `ur_gia`) when a filesystem change is detected.

Typical usage in this repo (examples are provided in `docker-compose.yml`):

  - To restart the runtime router (UR) when core code changes set:
      WATCH_PATHS=/code/core
      TARGET_CONTAINER=ur_gia

  - To restart the scheduling command when app code changes set:
      WATCH_PATHS=/code/app
      TARGET_CONTAINER=scheduling_gia

If you need to force a restart manually (for example to refresh a running
management command), you can "touch" any file under the watched path:

    docker compose exec <service> sh -c 'touch /code/core/__restart__'

The watcher ignores `__pycache__`, `.pyc` files and `.git` paths.
"""

import os
import subprocess
import time

from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer


class ChangeHandler(FileSystemEventHandler):
    def __init__(self):
        self.last_restart = 0
        self.restart_debounce = 2  # seconds

    def on_modified(self, event):
        self._check_and_restart(event.src_path)

    def on_created(self, event):
        self._check_and_restart(event.src_path)

    def on_deleted(self, event):
        self._check_and_restart(event.src_path)

    def _check_and_restart(self, path):
        # Ignore pycache and compiled files
        if "__pycache__" in path or ".pyc" in path or ".git" in path:
            return

        now = time.time()
        if now - self.last_restart < self.restart_debounce:
            return

        self.last_restart = now
        print(f'[{time.strftime("%H:%M:%S")}] Change detected: {path}', flush=True)
        time.sleep(1)
        self._restart_ur()

    def _restart_ur(self):
        # Determine target container from environment (default `ur_gia`)
        target = os.environ.get("TARGET_CONTAINER", "ur_gia")
        print(f'[{time.strftime("%H:%M:%S")}] Restarting {target}...', flush=True)
        # Try podman first (preferred in this setup), then docker
        cmd = f"podman restart {target} 2>/dev/null || docker restart {target} 2>/dev/null"
        result = subprocess.run(cmd, shell=True, capture_output=True)
        if result.returncode == 0:
            print(
                f'[{time.strftime("%H:%M:%S")}] {target} restarted successfully',
                flush=True,
            )
        else:
            print(f'[{time.strftime("%H:%M:%S")}] {target} restart failed', flush=True)
        time.sleep(1)


def main():
    handler = ChangeHandler()
    observer = Observer()

    # Allow overriding watched paths via environment variable `WATCH_PATHS`.
    # Default is `/code/core,/code/app` but you can set e.g. `WATCH_PATHS=/code/core`
    watch_paths_env = os.environ.get("WATCH_PATHS", "/code/core,/code/app")
    watch_paths = [p.strip() for p in watch_paths_env.split(",") if p.strip()]
    for path in watch_paths:
        if os.path.exists(path):
            observer.schedule(handler, path, recursive=True)
            print(f"Watching: {path}", flush=True)
        else:
            print(f"Not found (will not watch): {path}", flush=True)

    observer.start()
    print(
        f'[{time.strftime("%H:%M:%S")}] File watcher started. Monitoring for changes...',
        flush=True,
    )

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()


if __name__ == "__main__":
    main()
