import subprocess
import json
import os
from pathlib import Path
from datetime import datetime
import zipfile
import time
import traceback

from EmailMessagingGraph import send_mail

# ============================================================
# CONFIG
# ============================================================

GBAK = r"C:\Program Files\Firebird\Firebird_2_5_CGM\bin\gbak.exe"
FB_USER = "SYSDBA"
FB_PASS = "masterkey"
FB_PORT = "3050"

SRC_DIR = Path(r"c:\medicusext")
BACKUP_DIR = Path(r"U:\medicusbackup")

MAIL_TO = "vladimir.buzalka@buzalka.cz"

CHUNK = 8 * 1024 * 1024  # 8 MB


# ============================================================
# MAIN
# ============================================================

def main():
    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    now = datetime.now()
    ts = now.strftime("%Y-%m-%d_%H-%M-%S")

    # Find all FDB files (case-insensitive)
    fdb_files = sorted(SRC_DIR.glob("MEDICUS_FILES_*.fdb"))
    fdb_upper = sorted(SRC_DIR.glob("MEDICUS_FILES_*.FDB"))
    fdb_all = sorted(
        set(fdb_files + fdb_upper),
        key=lambda p: p.name.lower(),
    )

    backed_up = []
    errors = []
    fbk_paths = []  # FBK files to be zipped together

    # --------------------------------------------------------
    # 1) GBAK all databases
    # --------------------------------------------------------
    for fdb in fdb_all:
        name = fdb.stem
        fbk = BACKUP_DIR / f"{name}_{ts}.fbk"
        log = BACKUP_DIR / f"{name}_{ts}.log"

        result = {
            "file": fdb.name,
            "ok": False,
            "fbk_size": 0,
            "zip_size": 0,
            "t_gbak": 0,
            "t_zip": 0,
            "error": None,
        }

        try:
            print(f"GBAK: {fdb.name} ... ", end="", flush=True)
            t0 = time.time()
            db_conn = f"localhost/{FB_PORT}:{fdb}"
            cmd = [
                GBAK, "-b",
                "-user", FB_USER,
                "-pas", FB_PASS,
                db_conn, str(fbk),
                "-v",
            ]
            with open(log, "w", encoding="utf-8") as f:
                subprocess.run(
                    cmd, stdout=f, stderr=subprocess.STDOUT, check=True,
                )
            result["t_gbak"] = time.time() - t0
            result["fbk_size"] = fbk.stat().st_size
            print(f"OK ({result['t_gbak']:.0f}s)")

            # Delete log, keep FBK for zipping
            log.unlink()

            result["ok"] = True
            fbk_paths.append((fbk, result))
            backed_up.append(result)

        except Exception:
            result["error"] = traceback.format_exc()
            errors.append(result)
            for f in (fbk, log):
                if f.exists():
                    f.unlink()

    # --------------------------------------------------------
    # 2) ZIP all FBK files into one archive
    # --------------------------------------------------------
    total_zip_size = 0
    if fbk_paths:
        zip_path = BACKUP_DIR / f"MEDICUS_FILES_{ts}.zip"
        print(f"\nZIP: {zip_path.name}")
        t_zip_start = time.time()

        # Calculate total size for progress
        total_fbk_size = sum(fbk.stat().st_size for fbk, _ in fbk_paths)
        total_processed = 0

        with zipfile.ZipFile(
            zip_path, "w",
            compression=zipfile.ZIP_DEFLATED,
            compresslevel=9,
        ) as zf:
            for fbk, result in fbk_paths:
                zi = zipfile.ZipInfo(fbk.name)
                zi.compress_type = zipfile.ZIP_DEFLATED
                with zf.open(zi, "w", force_zip64=True) as z:
                    with open(fbk, "rb") as src:
                        while buf := src.read(CHUNK):
                            z.write(buf)
                            total_processed += len(buf)
                            pct = total_processed * 100 / total_fbk_size
                            print(
                                f"\r  {fbk.name}: {pct:6.2f}%",
                                end="", flush=True,
                            )
        print()

        t_zip_total = time.time() - t_zip_start
        total_zip_size = zip_path.stat().st_size
        print(f"ZIP OK ({t_zip_total:.0f}s, {total_zip_size/1024/1024:.1f} MB)")

        # Fill zip_size into each result and delete FBK files
        for fbk, result in fbk_paths:
            result["zip_size"] = total_zip_size
            fbk.unlink()

    # --------------------------------------------------------
    # Build report
    # --------------------------------------------------------
    report = []
    report.append(f"Backup externi DB - {now.strftime('%d.%m.%Y %H:%M')}")
    report.append(f"Celkem souboru: {len(fdb_all)}")
    report.append(f"Zalohovano: {len(backed_up)}")
    report.append(f"Chyby: {len(errors)}")
    report.append("")

    if backed_up:
        report.append("--- Backed up ---")
        total_fbk_mb = sum(r["fbk_size"] for r in backed_up) / 1024 / 1024
        for r in backed_up:
            report.append(
                f"  {r['file']}: "
                f"FBK {r['fbk_size']/1024/1024:.1f} MB "
                f"(gbak {r['t_gbak']:.0f}s)"
            )
        report.append(f"  Total FBK: {total_fbk_mb:.1f} MB -> ZIP: {total_zip_size/1024/1024:.1f} MB")
        report.append("")

    if errors:
        report.append("--- ERRORS ---")
        for r in errors:
            report.append(f"  {r['file']}: {r['error']}")
        report.append("")

    # Send email
    has_errors = len(errors) > 0
    subject = (
        f"{'X' if has_errors else 'OK'} "
        f"MEDICUS externi DB - "
        f"backup {len(backed_up)}/{len(fdb_all)}"
        f"{f', {len(errors)} errors' if has_errors else ''}"
    )

    send_mail(MAIL_TO, subject, "\n".join(report))

    print("\n" + "\n".join(report))

    if errors:
        raise RuntimeError(f"{len(errors)} backup(s) failed")


if __name__ == "__main__":
    main()
