"""Local allowlist for Obol detection flags.

Manages ~/.obol/allowlist.json — never leaves device.
Users can suppress specific detection flags for known-good patterns.

Zero external dependencies.
"""
import json
import os
import time

ALLOWLIST_PATH = os.path.expanduser("~/.obol/allowlist.json")

# Current schema version
_VERSION = 1


def _generate_id():
    """Generate a short unique rule ID."""
    import hashlib
    raw = f"{time.time()}{os.getpid()}".encode()
    return "r_" + hashlib.sha256(raw).hexdigest()[:8]


def load_allowlist():
    """Load allowlist from disk. Returns empty structure if missing."""
    try:
        if os.path.exists(ALLOWLIST_PATH):
            with open(ALLOWLIST_PATH) as f:
                data = json.load(f)
            if isinstance(data, dict) and data.get("version") == _VERSION:
                # Clean expired rules on load
                now = time.time()
                rules = data.get("rules", [])
                active = []
                for rule in rules:
                    if not isinstance(rule, dict):
                        continue
                    exp = rule.get("expires_at")
                    if exp and isinstance(exp, (int, float)) and exp < now:
                        continue  # Expired
                    active.append(rule)
                if len(active) != len(rules):
                    data["rules"] = active
                    _save_allowlist(data)
                return data
    except Exception:
        pass
    return {"version": _VERSION, "rules": []}


def _save_allowlist(data):
    """Atomically write allowlist to disk."""
    os.makedirs(os.path.dirname(ALLOWLIST_PATH), exist_ok=True)
    tmp = ALLOWLIST_PATH + ".tmp"
    fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
    with os.fdopen(fd, "w") as f:
        json.dump(data, f, indent=2)
    os.replace(tmp, ALLOWLIST_PATH)


def check_allowlist(flags, tool_name, target):
    """Filter flags through the allowlist.

    Returns list of flags that are NOT suppressed by any rule.
    """
    if not flags:
        return []

    data = load_allowlist()
    rules = data.get("rules", [])
    if not rules:
        return list(flags)

    remaining = []
    target_str = str(target or "").lower()

    for flag in flags:
        suppressed = False
        for rule in rules:
            if rule.get("flag") != flag:
                continue
            # Check tool filter
            rule_tool = rule.get("tool")
            if rule_tool and rule_tool != tool_name:
                continue
            # Check target_contains filter
            rule_target = rule.get("target_contains")
            if rule_target and rule_target.lower() not in target_str:
                continue
            # All filters matched — suppress this flag
            suppressed = True
            break
        if not suppressed:
            remaining.append(flag)

    return remaining


def add_rule(flag, tool=None, target_contains=None, note="", expires_days=None):
    """Add a new allowlist rule. Returns rule ID."""
    data = load_allowlist()

    rule = {
        "id": _generate_id(),
        "flag": flag,
        "created_at": time.time(),
        "expires_at": None,
    }
    if tool:
        rule["tool"] = tool
    if target_contains:
        rule["target_contains"] = target_contains
    if note:
        rule["note"] = note
    if expires_days and isinstance(expires_days, (int, float)) and expires_days > 0:
        rule["expires_at"] = time.time() + (expires_days * 86400)

    data["rules"].append(rule)
    _save_allowlist(data)
    return rule["id"]


def remove_rule(rule_id):
    """Remove a rule by ID. Returns True if found and removed."""
    data = load_allowlist()
    original_len = len(data["rules"])
    data["rules"] = [r for r in data["rules"] if r.get("id") != rule_id]
    if len(data["rules"]) < original_len:
        _save_allowlist(data)
        return True
    return False


def list_rules():
    """List all active (non-expired) allowlist rules."""
    data = load_allowlist()
    return data.get("rules", [])
