"""
Phase 94: Feature Flags for Gradual Rollouts.

Create feature flags with default states and rollout percentages.
Entity-level deterministic evaluation via hash bucketing, flag lifecycle
management, and usage statistics.

Storage:
  state/broker/feature_flags/flags.json    -- flag definitions
  state/broker/feature_flags/stats.json    -- per-flag check stats
"""

import hashlib
import json
import threading
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

from brynq.server import BROKER_DIR, _write_json_sync

# ── Paths ──────────────────────────────────────────────────────────────────

FF_DIR = BROKER_DIR / "feature_flags"
FF_DIR.mkdir(parents=True, exist_ok=True)

FLAGS_FILE = FF_DIR / "flags.json"
STATS_FILE = FF_DIR / "stats.json"

# ── Thread Safety ──────────────────────────────────────────────────────────

_lock = threading.Lock()

# ── Persistence Helpers ────────────────────────────────────────────────────


def _load_flags() -> dict:
    try:
        if FLAGS_FILE.exists():
            return json.loads(FLAGS_FILE.read_text("utf-8"))
    except (json.JSONDecodeError, OSError):
        pass
    return {}


def _save_flags(data: dict) -> None:
    _write_json_sync(FLAGS_FILE, data)


def _load_stats() -> dict:
    try:
        if STATS_FILE.exists():
            return json.loads(STATS_FILE.read_text("utf-8"))
    except (json.JSONDecodeError, OSError):
        pass
    return {}


def _save_stats(data: dict) -> None:
    _write_json_sync(STATS_FILE, data)


# ── Public API ─────────────────────────────────────────────────────────────


def create_flag(name: str, description: str = "", default: bool = False,
                rollout_pct: int = 0) -> dict:
    """Create a feature flag.

    *default*: the flag's global on/off state.
    *rollout_pct*: 0-100, the percentage of entities that see the flag enabled
                   when evaluated with an entity_id. 0 means rely on *default*.
    """
    if not (0 <= rollout_pct <= 100):
        return {"error": "rollout_pct must be between 0 and 100"}

    with _lock:
        flags = _load_flags()
        if name in flags:
            return {"error": f"Flag '{name}' already exists"}
        flags[name] = {
            "name": name,
            "description": description,
            "default": default,
            "rollout_pct": rollout_pct,
            "enabled": default,
            "created_at": datetime.now(timezone.utc).isoformat(),
            "updated_at": datetime.now(timezone.utc).isoformat(),
        }
        _save_flags(flags)
    return {"ok": True, "name": name}


def is_enabled(name: str, entity_id: Optional[str] = None) -> bool:
    """Check if a flag is on.

    Without entity_id: returns the flag's global enabled state.
    With entity_id: uses rollout_pct for deterministic hash-based evaluation.
    If rollout_pct is 0, falls back to the global enabled state.
    """
    with _lock:
        flags = _load_flags()
        flag = flags.get(name)
        if not flag:
            return False

        # Track stats
        stats = _load_stats()
        fs = stats.setdefault(name, {"checks": 0, "enabled_count": 0, "disabled_count": 0})
        fs["checks"] += 1

        result = flag.get("enabled", False)

        if entity_id and flag.get("rollout_pct", 0) > 0:
            # Hash-based deterministic rollout
            key = f"{name}:{entity_id}"
            h = int(hashlib.sha256(key.encode()).hexdigest(), 16)
            bucket = h % 100
            result = bucket < flag["rollout_pct"]

        if result:
            fs["enabled_count"] += 1
        else:
            fs["disabled_count"] += 1
        _save_stats(stats)

    return result


def enable(name: str) -> dict:
    """Globally enable a flag."""
    with _lock:
        flags = _load_flags()
        if name not in flags:
            return {"error": f"Flag '{name}' not found"}
        flags[name]["enabled"] = True
        flags[name]["updated_at"] = datetime.now(timezone.utc).isoformat()
        _save_flags(flags)
    return {"ok": True, "name": name, "enabled": True}


def disable(name: str) -> dict:
    """Globally disable a flag."""
    with _lock:
        flags = _load_flags()
        if name not in flags:
            return {"error": f"Flag '{name}' not found"}
        flags[name]["enabled"] = False
        flags[name]["updated_at"] = datetime.now(timezone.utc).isoformat()
        _save_flags(flags)
    return {"ok": True, "name": name, "enabled": False}


def set_rollout(name: str, percentage: int) -> dict:
    """Set the rollout percentage for a flag."""
    if not (0 <= percentage <= 100):
        return {"error": "percentage must be between 0 and 100"}
    with _lock:
        flags = _load_flags()
        if name not in flags:
            return {"error": f"Flag '{name}' not found"}
        flags[name]["rollout_pct"] = percentage
        flags[name]["updated_at"] = datetime.now(timezone.utc).isoformat()
        _save_flags(flags)
    return {"ok": True, "name": name, "rollout_pct": percentage}


def list_flags(status: Optional[str] = None) -> list[dict]:
    """List all flags, optionally filtered by status ('enabled'/'disabled')."""
    flags = _load_flags()
    result = list(flags.values())
    if status == "enabled":
        result = [f for f in result if f.get("enabled")]
    elif status == "disabled":
        result = [f for f in result if not f.get("enabled")]
    result.sort(key=lambda f: f.get("name", ""))
    return result


def get_flag(name: str) -> Optional[dict]:
    """Return a single flag definition."""
    return _load_flags().get(name)


def delete_flag(name: str) -> dict:
    """Delete a feature flag."""
    with _lock:
        flags = _load_flags()
        if name not in flags:
            return {"error": f"Flag '{name}' not found"}
        del flags[name]
        _save_flags(flags)
    return {"ok": True, "name": name, "deleted": True}


def get_flag_stats(name: str) -> dict:
    """Return usage stats for a flag: checks, enabled_count, disabled_count."""
    stats = _load_stats()
    fs = stats.get(name)
    if not fs:
        return {"name": name, "checks": 0, "enabled_count": 0, "disabled_count": 0}
    return {"name": name, **fs}
