#!/usr/bin/python3
# privacy.py: an honest, verifiable "right now" privacy report for the app. What is on, what runs ON
# this Mac vs what could touch an external service, and which privacy tier you are effectively in.
#
# It probes what it can see locally (is a local LLM reachable, are on-device transcription models
# present, is an iCloud export folder in use, is a backup folder chosen) and takes the few facts only
# the app knows (is an OpenAI key set, is the one-tap Maximum-privacy lockdown on, is calendar linked)
# as flags from main.swift. It NEVER makes an external call itself.
#
#   privacy.py --rec-dir DIR [--out-dir DIR] [--openai-set 0|1] [--max-privacy 0|1]
#              [--calendar 0|1] [--icloud-sync 0|1]
# Pure stdlib.
import sys, os, json

HOME = os.path.expanduser("~")
DEFAULT_REC = os.path.join(HOME, "Recordings/Epiphani")
ICLOUD = os.path.join(HOME, "Library/Mobile Documents/com~apple~CloudDocs/Epiphani")
HF_HUB = os.path.join(HOME, ".cache/huggingface/hub")

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
    import local_ai
except Exception:
    local_ai = None


def _flag(argv, name, default=False):
    if name in argv:
        i = argv.index(name)
        v = argv[i + 1] if i + 1 < len(argv) else "1"
        return v not in ("0", "false", "no", "off")
    return default


def _on_device_transcription():
    """True if a local transcription model (parakeet / mlx-whisper) is downloaded."""
    try:
        names = os.listdir(HF_HUB)
    except OSError:
        return False
    return any(("parakeet" in n or "whisper" in n) for n in names)


def _ollama_reachable():
    if local_ai is None:
        return False
    try:
        return bool(local_ai.installed_models()) or local_ai._reachable()
    except Exception:
        return False


def _last_backup(out_dir):
    if not out_dir:
        return None
    p = os.path.join(out_dir, ".epiphani-backup.json")
    try:
        with open(p, encoding="utf-8") as f:
            return json.load(f).get("last_backup")
    except (OSError, ValueError):
        return None


NO_TRAIN = "OpenAI does not use API data to train its models."


def status(rec_dir, out_dir, openai_set, max_privacy, calendar_on, icloud_sync,
           transcribe_engine="cloud", summarize_engine="cloud"):
    """The single source of truth for BOTH the Privacy tier banner and the per-feature toggle
    settings surface. Each of Transcription / Diary summaries / Calendar / Cloud sync is settable
    independently (transcribe_engine and summarize_engine are two SEPARATE dials, not one combined
    org engine) and Maximum privacy is the master switch that forces all four to their most private
    state at once. `settable` + `key` tell the UI which rows are real controls (bound to
    main.swift's `set(key, val)` bridge) vs. read-only status (Recording, Backup, the network
    summary)."""
    ollama = _ollama_reachable()
    on_dev_tx = _on_device_transcription()
    last_backup = _last_backup(out_dir)

    # Maximum privacy forces every settable item to its most private state, same as it always has.
    transcribe_local = bool(max_privacy) or transcribe_engine == "local"
    summarize_local = bool(max_privacy) or summarize_engine == "local"
    calendar_on = bool(calendar_on) and not max_privacy
    icloud_sync = bool(icloud_sync) and not max_privacy

    # Cloud is only ACTIVE (not just selected) when a key is actually set; otherwise the feature is
    # simply unconfigured, never silently treated as "on".
    transcribe_cloud_active = (not transcribe_local) and bool(openai_set)
    summarize_cloud_active = (not summarize_local) and bool(openai_set)
    external_possible = transcribe_cloud_active or summarize_cloud_active or icloud_sync or calendar_on

    if not external_possible:
        tier = "Maximum privacy"
        tier_detail = "On-device only. Nothing leaves this Mac."
    elif not (transcribe_cloud_active or summarize_cloud_active) and (icloud_sync or calendar_on):
        tier = "On-device AI, local sync"
        tier_detail = "AI runs on this Mac; only your chosen sync/calendar touches Apple services."
    else:
        tier = "Cloud-assisted"
        tier_detail = "Transcription and/or summaries use the OpenAI API. Turn on the lockdown for on-device only."

    if transcribe_cloud_active:
        transcribe_detail = "Sent to OpenAI to produce your transcript. " + NO_TRAIN
    elif transcribe_local and on_dev_tx:
        transcribe_detail = "Transcribed entirely on this Mac; nothing is sent anywhere."
    elif transcribe_local:
        transcribe_detail = "Transcribed entirely on this Mac once a local model finishes downloading."
    else:
        transcribe_detail = "No transcription configured. Add an OpenAI key, or switch to on-device."

    if summarize_cloud_active:
        summarize_detail = ("Diary titles, summaries, and action items are sent to OpenAI. " + NO_TRAIN +
                             " Ask (the Q&A view) always answers on-device, regardless of this setting.")
    elif summarize_local and ollama:
        summarize_detail = ("Diary titles, summaries, and action items are written entirely on this Mac. "
                             "Ask already always runs on-device.")
    elif summarize_local:
        summarize_detail = ("Diary titles/summaries run on this Mac once you install a local model in "
                             "the Models tab. Ask already runs on-device even without one (a simpler "
                             "keyword match).")
    else:
        summarize_detail = "No summarizer configured. Add an OpenAI key, or switch to on-device."

    audit = [
        {"item": "Recording", "key": None, "settable": False, "locked": False,
         "where": "On this Mac", "on": True,
         "detail": "Audio is written only to your recordings folder."},
        {"item": "Transcription", "key": "transcribeEngine", "settable": True, "locked": bool(max_privacy),
         "engine": "local" if transcribe_local else "cloud",
         "on": transcribe_cloud_active or transcribe_local,
         "where": "Cloud (OpenAI)" if transcribe_cloud_active else ("On this Mac" if transcribe_local else "Not set up"),
         "detail": transcribe_detail},
        {"item": "Diary summaries / Ask", "key": "summarizeEngine", "settable": True, "locked": bool(max_privacy),
         "engine": "local" if summarize_local else "cloud",
         "on": summarize_cloud_active or summarize_local,
         "where": ("Cloud (OpenAI)" if summarize_cloud_active
                   else ("On this Mac" if summarize_local else "Not set up")),
         "detail": summarize_detail},
        {"item": "Calendar auto-naming", "key": "calendarOn", "settable": True, "locked": bool(max_privacy),
         "on": calendar_on, "where": "Apple Calendar" if calendar_on else "Off",
         "detail": ("Reads today's events on this Mac (never sent anywhere) to suggest a name for each "
                    "recording; you confirm before anything renames." if calendar_on
                    else "Off. RecapMuse does not read your calendar.")},
        {"item": "Cloud sync (iCloud)", "key": "icloudSync", "settable": True, "locked": bool(max_privacy),
         "on": icloud_sync, "where": "iCloud Drive" if icloud_sync else "Off",
         "detail": ("Your conversation list (titles, summaries, action items — never audio) is "
                    "written to iCloud Drive so your iPhone can read it in Files. Apple, not OpenAI, "
                    "handles this data." if icloud_sync else "Off. Nothing is copied to iCloud.")},
        {"item": "Backup", "key": None, "settable": False, "locked": False, "on": bool(out_dir),
         "where": "Folder you chose" if out_dir else "Off",
         "detail": ("Last backup %s." % last_backup) if last_backup else
                   ("Folder set; not yet backed up." if out_dir else "No backup folder chosen.")},
        {"item": "Network used by the app itself", "key": None, "settable": False, "locked": False,
         "on": external_possible, "where": "Possible" if external_possible else "None",
         "detail": "Only the integrations above can reach a network." if external_possible
                   else "The app makes no external calls in this state."},
    ]

    guarantee = ("Audio never leaves this Mac. " +
                 ("In Maximum privacy, transcription and summaries run on-device too, and the app makes "
                  "no external calls at all." if tier == "Maximum privacy" else
                  "You can switch to Maximum privacy (on-device only) at any time with one tap."))

    return {"ok": True, "tier": tier, "tier_detail": tier_detail, "guarantee": guarantee,
            "max_privacy": bool(max_privacy), "external_possible": external_possible,
            "can_go_local": bool(ollama or on_dev_tx),    # is a fully on-device path even available?
            "transcribe_engine": "local" if transcribe_local else "cloud",
            "summarize_engine": "local" if summarize_local else "cloud",
            "calendar_on": calendar_on, "icloud_sync": icloud_sync,
            "audit": audit}


def main(argv):
    rec_dir = os.path.expanduser(argv[argv.index("--rec-dir") + 1]) if "--rec-dir" in argv else DEFAULT_REC
    out_dir = os.path.expanduser(argv[argv.index("--out-dir") + 1]) if "--out-dir" in argv else ""
    rep = status(rec_dir, out_dir,
                 openai_set=_flag(argv, "--openai-set"),
                 max_privacy=_flag(argv, "--max-privacy"),
                 calendar_on=_flag(argv, "--calendar"),
                 icloud_sync=_flag(argv, "--icloud-sync"),
                 transcribe_engine=(argv[argv.index("--transcribe-engine") + 1]
                                    if "--transcribe-engine" in argv else "cloud"),
                 summarize_engine=(argv[argv.index("--summarize-engine") + 1]
                                   if "--summarize-engine" in argv else "cloud"))
    sys.stdout.write(json.dumps(rep, ensure_ascii=False))


if __name__ == "__main__":
    main(sys.argv[1:])
