#!/usr/bin/env python3
"""
Forwarder (VPS) — dopycha zdjecia ze spoola na DOMOWY serwer przez WireGuard.

Dziala jako osobny, pojedynczy proces (wlasna usluga systemd), zeby uniknac
wyscigow miedzy workerami gunicorna. W petli:
  - bierze zdjecia z forwarded=0, ktore maja lokalna pelna kopie,
  - wysyla je na dom (POST /recv); po sukcesie ustawia forwarded=1
    i (domyslnie) kasuje lokalna pelna kopie — zostaje tylko miniatura,
  - realizuje zalegle kasowania z kolejki pending_deletes,
  - gdy dom offline: pomija, sprobuje w kolejnym cyklu.

Zmienne srodowiskowe:
  WR_HOME_URL           np. http://10.50.0.2:8021
  WR_HOME_SECRET        wspolny sekret z domowym serwerem
  WR_KEEP_FULL_ON_VPS   '1' = zostaw pelne kopie na VPS jako cache (domyslnie 0)
  WR_FORWARD_INTERVAL   co ile sekund cykl (domyslnie 10)
"""

import os
import time
import sqlite3
from pathlib import Path

import requests

BASE_DIR   = Path(__file__).resolve().parent
UPLOAD_DIR = BASE_DIR / "uploads"
DB_PATH    = BASE_DIR / "wr.db"

HOME_URL   = os.environ.get("WR_HOME_URL", "").rstrip("/")
HOME_SECRET= os.environ.get("WR_HOME_SECRET", "")
KEEP_FULL  = os.environ.get("WR_KEEP_FULL_ON_VPS", "0") == "1"
INTERVAL   = int(os.environ.get("WR_FORWARD_INTERVAL", "10"))

HEADERS = {"X-WR-Secret": HOME_SECRET}


def db():
    conn = sqlite3.connect(DB_PATH, timeout=10)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    return conn


def forward_one(row):
    name = row["filename"]
    path = UPLOAD_DIR / name
    if not path.exists():
        # pliku brak (np. skasowany) — uznaj za zalatwione
        return True
    try:
        with open(path, "rb") as fh:
            r = requests.post(
                f"{HOME_URL}/recv",
                headers=HEADERS,
                files={"photo": (name, fh, "image/jpeg")},
                data={"name": name},
                timeout=30,
            )
        if r.status_code == 200:
            if not KEEP_FULL:
                try:
                    path.unlink()
                except FileNotFoundError:
                    pass
            return True
    except Exception as e:
        print(f"[forwarder] dom niedostepny przy {name}: {e}", flush=True)
    return False


def process_deletes(conn):
    rows = conn.execute("SELECT name FROM pending_deletes").fetchall()
    for r in rows:
        name = r["name"]
        try:
            resp = requests.post(
                f"{HOME_URL}/del/{name}", headers=HEADERS, timeout=10
            )
            if resp.status_code in (200, 404):
                conn.execute("DELETE FROM pending_deletes WHERE name=?", (name,))
                conn.commit()
        except Exception:
            return  # dom offline, sprobuj pozniej


def cycle():
    with db() as conn:
        rows = conn.execute(
            "SELECT * FROM photos WHERE forwarded=0 ORDER BY id ASC"
        ).fetchall()
        for row in rows:
            if forward_one(row):
                conn.execute(
                    "UPDATE photos SET forwarded=1 WHERE id=?", (row["id"],)
                )
                conn.commit()
            else:
                break  # dom offline — przerwij, reszta poczeka
        process_deletes(conn)


def main():
    if not HOME_URL:
        print("[forwarder] WR_HOME_URL nie ustawione — nic do roboty.", flush=True)
    print("[forwarder] start", flush=True)
    while True:
        try:
            if HOME_URL:
                cycle()
        except Exception as e:
            print(f"[forwarder] blad cyklu: {e}", flush=True)
        time.sleep(INTERVAL)


if __name__ == "__main__":
    main()
