#!/usr/bin/env python3
"""Multi-browser AnimePahe cookie reader.

Commands:
  detect   Scan all browsers for animepahe cookies
  read     Read cookies from a specific browser
  clear    Delete animepahe cookies from a specific browser

Output is JSON to stdout.
"""
import argparse
import json
import os
import shutil
import sqlite3
import sys
import tempfile

# ── Browser cookie locations ──────────────────────────────────────────
FIREFOX_DIRS = [os.path.expanduser("~/.mozilla/firefox")]
CHROMIUM_BASED = [
    ("chrome", os.path.expanduser("~/.config/google-chrome")),
    ("chromium", os.path.expanduser("~/.config/chromium")),
    ("brave", os.path.expanduser("~/.config/BraveSoftware/Brave-Browser")),
    ("edge", os.path.expanduser("~/.config/microsoft-edge")),
]

DOMAIN_FILTERS = ["%animepahe%", "%pahe%"]

# ── Helpers ───────────────────────────────────────────────────────────

def _find_firefox_profiles():
    profiles = []
    for d in FIREFOX_DIRS:
        if not os.path.isdir(d):
            continue
        for entry in os.listdir(d):
            db_path = os.path.join(d, entry, "cookies.sqlite")
            if os.path.isfile(db_path):
                profiles.append({"profile": entry, "path": db_path})
    return profiles


def _read_firefox_cookies(db_path):
    tmp = tempfile.mktemp(suffix=".sqlite")
    try:
        shutil.copy2(db_path, tmp)
        wal = db_path + "-wal"
        if os.path.isfile(wal):
            shutil.copy2(wal, tmp + "-wal")
        conn = sqlite3.connect(tmp)
        cursor = conn.cursor()
        cursor.execute(
            "SELECT name, value FROM moz_cookies WHERE host LIKE ? OR host LIKE ?",
            DOMAIN_FILTERS,
        )
        rows = cursor.fetchall()
        conn.close()
        return {name: val for name, val in rows}
    except Exception as e:
        return {"_error": str(e)}
    finally:
        try: os.unlink(tmp)
        except: pass
        try: os.unlink(tmp + "-wal")
        except: pass


def _find_chromium_profiles(browser_name, base_dir):
    profiles = []
    if not os.path.isdir(base_dir):
        return profiles
    for entry in os.listdir(base_dir):
        cookie_path = os.path.join(base_dir, entry, "Cookies")
        local_state = os.path.join(base_dir, "Local State")
        if os.path.isfile(cookie_path):
            profiles.append({
                "profile": entry,
                "path": cookie_path,
                "local_state": local_state if os.path.isfile(local_state) else None,
            })
    return profiles


def _decrypt_chromium_cookies(browser_name, profile):
    """Attempt to decrypt cookies from a Chromium-based browser.
    
    Returns dict of cookie name→value, or dict with _error key.
    Returns empty dict on failure — caller should fall back gracefully.
    """
    # For now, this is a best-effort attempt that may fail on modern
    # setups (portal-based encryption on KDE, etc.)
    return {}


def _read_chromium_cookies(browser_name, profile):
    tmp = tempfile.mktemp(suffix=".sqlite")
    try:
        shutil.copy2(profile["path"], tmp)
        conn = sqlite3.connect(tmp)
        cursor = conn.cursor()
        cursor.execute(
            "SELECT name, value, encrypted_value FROM cookies "
            "WHERE host_key LIKE ? OR host_key LIKE ?",
            DOMAIN_FILTERS,
        )
        rows = cursor.fetchall()
        conn.close()

        cookies = {}
        for name, value, enc_val in rows:
            if value:
                cookies[name] = value
            else:
                if enc_val and enc_val[:3] in (b"v10", b"v11"):
                    try_decrypt = _decrypt_chromium_cookies(browser_name, profile)
                    if name in try_decrypt:
                        cookies[name] = try_decrypt[name]
        return cookies
    except Exception as e:
        return {"_error": str(e)}
    finally:
        try: os.unlink(tmp)
        except: pass


def _cookie_map_to_string(cookies):
    parts = []
    for k, v in cookies.items():
        if k.startswith("_"):
            continue
        parts.append(f"{k}={v}")
    return "; ".join(parts)


# ── Commands ──────────────────────────────────────────────────────────

def cmd_detect():
    result = {}

    # Firefox
    ff_profiles = _find_firefox_profiles()
    ff_cookies = {}
    for p in ff_profiles:
        c = _read_firefox_cookies(p["path"])
        if "_error" not in c and c:
            ff_cookies.update(c)
    if ff_cookies:
        result["firefox"] = {
            "found": True,
            "profiles": [p["profile"] for p in ff_profiles],
            "cookies": {k: v for k, v in ff_cookies.items() if not k.startswith("_")},
            "cookie_string": _cookie_map_to_string(ff_cookies),
        }
    else:
        result["firefox"] = {"found": False, "profiles": [p["profile"] for p in ff_profiles]}

    # Chromium-based
    for name, base_dir in CHROMIUM_BASED:
        profiles = _find_chromium_profiles(name, base_dir)
        if not profiles:
            result[name] = {"found": False, "error": "No profiles found"}
            continue
        all_cookies = {}
        for p in profiles:
            c = _read_chromium_cookies(name, p)
            if "_error" not in c and c:
                all_cookies.update(c)
        encrypted = any(p["path"] for p in profiles)
        if all_cookies:
            result[name] = {
                "found": True,
                "profiles": [p["profile"] for p in profiles],
                "cookies": {k: v for k, v in all_cookies.items() if not k.startswith("_")},
                "cookie_string": _cookie_map_to_string(all_cookies),
                "encrypted": encrypted,
            }
        else:
            result[name] = {
                "found": False,
                "profiles": [p["profile"] for p in profiles],
                "encrypted": encrypted,
                "error": "Cookies found but could not be decrypted (try manual paste)",
            }

    return result


def cmd_read(browser, profile_name=None):
    if browser == "firefox":
        for p in _find_firefox_profiles():
            if profile_name and p["profile"] != profile_name:
                continue
            c = _read_firefox_cookies(p["path"])
            if "_error" not in c and c:
                return {
                    "browser": "firefox",
                    "profile": p["profile"],
                    "cookies": {k: v for k, v in c.items() if not k.startswith("_")},
                    "cookie_string": _cookie_map_to_string(c),
                }
        return {"browser": "firefox", "error": "No cookies found"}

    for name, base_dir in CHROMIUM_BASED:
        if name != browser:
            continue
        for p in _find_chromium_profiles(name, base_dir):
            if profile_name and p["profile"] != profile_name:
                continue
            c = _read_chromium_cookies(name, p)
            if "_error" not in c and c:
                return {
                    "browser": name,
                    "profile": p["profile"],
                    "cookies": {k: v for k, v in c.items() if not k.startswith("_")},
                    "cookie_string": _cookie_map_to_string(c),
                }
            if c:
                return {"browser": name, "error": "Found but could not decrypt"}
        return {"browser": name, "error": "No cookies found"}

    return {"error": f"Unknown browser: {browser}"}


def cmd_clear(browser):
    if browser == "firefox":
        cleared = []
        locked = []
        for p in _find_firefox_profiles():
            try:
                conn = sqlite3.connect(p["path"])
                conn.execute(
                    "DELETE FROM moz_cookies WHERE host LIKE ? OR host LIKE ?",
                    DOMAIN_FILTERS,
                )
                conn.commit()
                conn.close()
                cleared.append(p["profile"])
            except Exception as e:
                locked.append({"profile": p["profile"], "error": str(e)})
        return {"ok": True, "cleared": cleared, "locked": locked}

    for name, base_dir in CHROMIUM_BASED:
        if name != browser:
            continue
        cleared = []
        locked = []
        for p in _find_chromium_profiles(name, base_dir):
            try:
                conn = sqlite3.connect(p["path"])
                conn.execute(
                    "DELETE FROM cookies WHERE host_key LIKE ? OR host_key LIKE ?",
                    DOMAIN_FILTERS,
                )
                conn.commit()
                conn.close()
                cleared.append(p["profile"])
            except Exception as e:
                locked.append({"profile": p["profile"], "error": str(e)})
        return {"ok": True, "cleared": cleared, "locked": locked}

    return {"error": f"Unknown browser: {browser}"}


# ── CLI ───────────────────────────────────────────────────────────────

def main():
    parser = argparse.ArgumentParser(description="Read AnimePahe cookies from browsers")
    parser.add_argument("command", choices=["detect", "read", "clear"])
    parser.add_argument("--browser", "-b", help="Browser name (firefox, chrome, brave, edge)")
    parser.add_argument("--profile", "-p", help="Profile name (optional)")
    args = parser.parse_args()

    if args.command == "detect":
        result = cmd_detect()
    elif args.command == "read":
        if not args.browser:
            print(json.dumps({"error": "--browser is required for read"}))
            sys.exit(1)
        result = cmd_read(args.browser, args.profile)
    elif args.command == "clear":
        if not args.browser:
            print(json.dumps({"error": "--browser is required for clear"}))
            sys.exit(1)
        result = cmd_clear(args.browser)

    print(json.dumps(result, indent=2))


if __name__ == "__main__":
    main()
