"""
Lucas Weiss Photography — Windows Folder Watcher
Watches local folders and auto-uploads new images to the server.

Setup:
  1. pip install watchdog requests
  2. Edit config.json with your server URL and API credentials
  3. Run: python watcher.py
  4. To run on startup: add to Windows Task Scheduler
"""

import os
import sys
import time
import json
import logging
import requests
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

# ── CONFIG ────────────────────────────────────────────────────
CONFIG_PATH = Path(__file__).parent / 'config.json'

def load_config():
    with open(CONFIG_PATH, 'r') as f:
        return json.load(f)

# ── LOGGING ───────────────────────────────────────────────────
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s  %(levelname)s  %(message)s',
    handlers=[
        logging.FileHandler('watcher.log', encoding='utf-8'),
        logging.StreamHandler(sys.stdout),
    ]
)
log = logging.getLogger(__name__)

# ── AUTH ──────────────────────────────────────────────────────
_token = None

def get_token(config):
    global _token
    if _token:
        return _token
    try:
        res = requests.post(
            f"{config['server_url']}/api/login",
            json={
                'username': config['admin_username'],
                'password': config['admin_password'],
            },
            timeout=10
        )
        res.raise_for_status()
        _token = res.json()['token']
        log.info('Authenticated with server')
        return _token
    except Exception as e:
        log.error(f'Auth failed: {e}')
        return None

def reset_token():
    global _token
    _token = None

# ── UPLOAD ────────────────────────────────────────────────────
ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.webp'}

def upload_file(filepath, category_id, config):
    path = Path(filepath)
    if path.suffix.lower() not in ALLOWED_EXTENSIONS:
        return

    # Wait briefly to ensure file is fully written
    time.sleep(1.5)

    token = get_token(config)
    if not token:
        log.error(f'No auth token — skipping {path.name}')
        return

    log.info(f'Uploading {path.name} → {category_id}')

    try:
        with open(filepath, 'rb') as f:
            res = requests.post(
                f"{config['server_url']}/api/admin/upload/{category_id}",
                headers={'Authorization': f'Bearer {token}'},
                files={'photos': (path.name, f, 'image/jpeg')},
                timeout=120
            )

        if res.status_code == 401:
            log.warning('Token expired — re-authenticating')
            reset_token()
            upload_file(filepath, category_id, config)
            return

        res.raise_for_status()
        data = res.json()
        log.info(f'Uploaded {path.name} successfully — {data.get("uploaded", 1)} photo(s)')

        # Optionally move to a "uploaded" subfolder to avoid re-processing
        if config.get('move_after_upload', False):
            done_dir = path.parent / 'uploaded'
            done_dir.mkdir(exist_ok=True)
            dest = done_dir / path.name
            path.rename(dest)
            log.info(f'Moved {path.name} to uploaded/')

    except requests.exceptions.RequestException as e:
        log.error(f'Upload failed for {path.name}: {e}')
    except Exception as e:
        log.error(f'Unexpected error uploading {path.name}: {e}')

# ── FILE WATCHER ──────────────────────────────────────────────
class PhotoHandler(FileSystemEventHandler):
    def __init__(self, category_id, config):
        self.category_id = category_id
        self.config      = config
        self._processing = set()

    def on_created(self, event):
        if event.is_directory:
            return
        path = Path(event.src_path)
        if path.suffix.lower() not in ALLOWED_EXTENSIONS:
            return
        if str(path) in self._processing:
            return
        self._processing.add(str(path))
        try:
            upload_file(str(path), self.category_id, self.config)
        finally:
            self._processing.discard(str(path))

# ── MAIN ──────────────────────────────────────────────────────
def main():
    config = load_config()
    observers = []

    log.info('Lucas Weiss Photography — Folder Watcher starting')
    log.info(f'Server: {config["server_url"]}')

    for watch in config['watches']:
        folder      = Path(watch['folder'])
        category_id = watch['category_id']

        if not folder.exists():
            log.warning(f'Folder does not exist, creating: {folder}')
            folder.mkdir(parents=True, exist_ok=True)

        handler  = PhotoHandler(category_id, config)
        observer = Observer()
        observer.schedule(handler, str(folder), recursive=False)
        observer.start()
        observers.append(observer)
        log.info(f'Watching {folder} → category: {category_id}')

    log.info('Watcher running — press Ctrl+C to stop')

    try:
        while True:
            time.sleep(5)
            # Check all observers are still alive
            for obs in observers:
                if not obs.is_alive():
                    log.error('An observer died — restarting')
                    obs.start()
    except KeyboardInterrupt:
        log.info('Stopping watcher...')
        for obs in observers:
            obs.stop()
        for obs in observers:
            obs.join()
        log.info('Watcher stopped')

if __name__ == '__main__':
    main()
