#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Nightly Medicus automatic restore
---------------------------------
Workflow:
1️⃣ Kill all Medicus processes
2️⃣ Restart Firebird service (to release all open connections)
3️⃣ Clean restore folder
4️⃣ Find and extract newest Medicus backup (.fbk or .zip)
5️⃣ Restore database using gbak (Firebird service kept running)
6️⃣ Restart Firebird again to refresh state
7️⃣ Log all actions with timestamps
"""

import os
import psutil
import shutil
import zipfile
import subprocess
from pathlib import Path
from datetime import datetime
import time

# =========================================
# 🔧 CONFIGURATION
# =========================================
FIREBIRD_GBAK = Path(r"C:\Program Files\Firebird\Firebird_2_5_CGM\bin\gbak.exe")
BACKUP_DIR = Path(r"G:\OnedriveOrdinace\OneDrive\MedicusBackup")
RESTORE_DIR = Path(r"Z:\Medicus 3\restore")
TARGET_DB = Path(r"Z:\Medicus 3\data\MEDICUS.FDB")
SERVICE_NAME = "FirebirdServerCGM"
USER = "SYSDBA"
PASSWORD = "masterkey"

LOG_FILE = RESTORE_DIR / f"nightly_restore_{datetime.now():%Y%m%d_%H%M%S}.log"

# =========================================
# 🪓 1) Kill Medicus processes
# =========================================
def kill_medicus():
    targets = ["Medicus.exe", "MedicusServer.exe", "MedicusUpdater.exe"]
    with open(LOG_FILE, "w", encoding="utf-8") as log:
        log.write(f"==== NIGHTLY RESTORE START {datetime.now():%Y-%m-%d %H:%M:%S} ====\n")
        for proc in psutil.process_iter(["name", "pid"]):
            try:
                if proc.info["name"] and proc.info["name"].lower() in [t.lower() for t in targets]:
                    log.write(f"Killing {proc.info['name']} (PID {proc.pid})\n")
                    proc.kill()
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                continue
        log.write("All Medicus processes terminated.\n")

# =========================================
# ⚙️ 2) Firebird service control
# =========================================
def service_cmd(action):
    cmd = f'net {action} "{SERVICE_NAME}"'
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.returncode == 0, result.stdout + result.stderr

def restart_firebird(label):
    ok, out = service_cmd("stop")
    with open(LOG_FILE, "a", encoding="utf-8") as log:
        log.write(f"\n--- Restart Firebird ({label}) ---\n")
        log.write(f"Stopping {SERVICE_NAME}...\n{out}\n")
    time.sleep(5)
    ok, out = service_cmd("start")
    with open(LOG_FILE, "a", encoding="utf-8") as log:
        log.write(f"Starting {SERVICE_NAME}...\n{out}\n")

# =========================================
# 🧹 3) Clean restore folder
# =========================================
def clean_restore_folder():
    for item in RESTORE_DIR.glob("*"):
        try:
            if item.is_file():
                item.unlink()
            elif item.is_dir():
                shutil.rmtree(item)
        except Exception as e:
            with open(LOG_FILE, "a", encoding="utf-8") as log:
                log.write(f"⚠️ Could not delete {item}: {e}\n")

# =========================================
# 💾 4) Find newest backup and prepare .fbk
# =========================================
def prepare_backup():
    backups = list(BACKUP_DIR.glob("Medicus_*.fbk")) + list(BACKUP_DIR.glob("Medicus_*.zip"))
    if not backups:
        raise SystemExit(f"❌ No Medicus backups found in {BACKUP_DIR}")

    latest = max(backups, key=lambda f: f.stat().st_mtime)
    fbk_path = RESTORE_DIR / (latest.stem + ".fbk")

    with open(LOG_FILE, "a", encoding="utf-8") as log:
        log.write(f"\nUsing backup: {latest}\n")

    if latest.suffix.lower() == ".fbk":
        shutil.copy2(latest, fbk_path)
    else:
        with zipfile.ZipFile(latest, "r") as zf:
            fbk_files = [n for n in zf.namelist() if n.lower().endswith(".fbk")]
            if not fbk_files:
                raise SystemExit("❌ ZIP file does not contain .fbk!")
            zf.extract(fbk_files[0], RESTORE_DIR)
            extracted = RESTORE_DIR / fbk_files[0]
            if extracted != fbk_path:
                shutil.move(extracted, fbk_path)

    return fbk_path

# =========================================
# 🧩 5) Restore database using gbak
# =========================================
def restore_database(fbk_path):
    cmd = f'"{FIREBIRD_GBAK}" -rep "{fbk_path}" "{TARGET_DB}" -user {USER} -pas {PASSWORD}'
    with open(LOG_FILE, "a", encoding="utf-8") as log:
        log.write(f"\nRunning restore command:\n{cmd}\n")

    result = subprocess.run(cmd, shell=True, text=True, capture_output=True)

    with open(LOG_FILE, "a", encoding="utf-8") as log:
        log.write(result.stdout)
        log.write(result.stderr)
        log.write(f"Return code: {result.returncode}\n")

    if result.returncode == 0:
        print("✅ Medicus database successfully restored!")
    else:
        print("❌ Restore failed! Check log for details.")

# =========================================
# 🚀 MAIN WORKFLOW
# =========================================
if __name__ == "__main__":
    kill_medicus()
    restart_firebird("before restore")
    clean_restore_folder()
    fbk = prepare_backup()
    restore_database(fbk)
    restart_firebird("after restore")

    with open(LOG_FILE, "a", encoding="utf-8") as log:
        log.write(f"\n==== RESTORE COMPLETED {datetime.now():%Y-%m-%d %H:%M:%S} ====\n")

    print(f"📝 Full log saved to {LOG_FILE}")
