"""MuraveyChina v2 License Client — server-synced licensing with free cutoff + paid subscription.

Responsibilities:
- Register machine_id with server on first launch
- Sync status (free/paid/expired) on every launch
- Cache data locally for offline use (7-day grace)
- Provide check_access() for app to decide if user can proceed
"""

from __future__ import annotations

import hashlib
import hmac
import json
import logging
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

log = logging.getLogger(__name__)

_SERVER_URL = "https://license.muravey.app"
_CACHE_FILE = "license_v2.json"
def _get_hmac_key() -> bytes:
    """Obfuscated HMAC key for local cache integrity."""
    from ..seb.obfuscation import deobfuscate
    return deobfuscate("FTlPFB1bFRpCAU4ANAZaYU4zaA==").encode()

_HMAC_KEY = _get_hmac_key()
_OFFLINE_GRACE_DAYS = 7


class LicenseV2Client:
    """Client for v2 licensing system."""

    def __init__(self, data_dir: str | Path | None = None):
        if data_dir is None:
            data_dir = Path.home() / ".muravey-china"
        self._data_dir = Path(data_dir)
        self._data_dir.mkdir(parents=True, exist_ok=True)
        self._cache_path = self._data_dir / _CACHE_FILE
        self._data: dict[str, Any] = {}
        self._load_cache()

    # ── Public API ──

    def check_access(self) -> tuple[str, dict]:
        """Check if user has access. Returns (status, info).

        status: 'paid' | 'free' | 'expired'
        info: dict with dates, referral_code, pricing, etc.
        """
        now = datetime.now(timezone.utc).strftime("%Y-%m-%d")

        paid_until = self._data.get("paid_access_until")
        if paid_until and paid_until >= now:
            return "paid", self._data

        cutoff = self._data.get("free_cutoff_date", "2026-05-24")
        if cutoff >= now:
            return "free", self._data

        return "expired", self._data

    def register_and_sync(self, machine_id: str, email: str = "", referral_code: str = "") -> bool:
        """Register with server (if new) and sync status. Returns True if online sync succeeded."""
        import urllib.request
        import urllib.error

        self._data["machine_id"] = machine_id

        # Try register first
        try:
            payload = json.dumps({
                "machine_id": machine_id,
                "email": email,
                "referral_code_used": referral_code,
            }).encode()

            req = urllib.request.Request(
                f"{_SERVER_URL}/api/v2/register",
                data=payload,
                headers={"Content-Type": "application/json", "User-Agent": "MuraveyChina/2.0"},
                method="POST",
            )
            with urllib.request.urlopen(req, timeout=10) as resp:
                data = json.loads(resp.read())

            if data.get("ok"):
                self._data.update({
                    "referral_code": data.get("referral_code"),
                    "status": data.get("status", "free"),
                    "free_cutoff_date": data.get("free_cutoff_date"),
                    "paid_access_until": data.get("paid_access_until"),
                    "referral_balance": data.get("referral_balance", 0),
                    "last_sync": datetime.now(timezone.utc).isoformat(),
                })
                self._save_cache()
                log.info("License v2: registered/synced — status=%s", self._data.get("status"))
                return True

        except Exception as exc:
            log.warning("License v2: register failed — %s", exc)

        # Try sync as fallback (already registered)
        return self._sync(machine_id)

    def sync(self, machine_id: str) -> bool:
        """Sync with server. Returns True if succeeded."""
        return self._sync(machine_id)

    def get_referral_code(self) -> str:
        return self._data.get("referral_code", "")

    def get_referral_balance(self) -> int:
        return self._data.get("referral_balance", 0)

    def get_free_cutoff_date(self) -> str:
        return self._data.get("free_cutoff_date", "2026-05-24")

    def get_paid_until(self) -> str | None:
        return self._data.get("paid_access_until")

    def get_pricing(self) -> dict:
        return self._data.get("pricing", {"tier": "unknown", "base_price": 150, "discount": 0, "final_price": 150})

    def is_offline_ok(self) -> bool:
        """Check if offline cache is within grace period."""
        last_sync = self._data.get("last_sync")
        if not last_sync:
            return False
        try:
            sync_time = datetime.fromisoformat(last_sync.replace("Z", "+00:00"))
            age_days = (datetime.now(timezone.utc) - sync_time).days
            return age_days <= _OFFLINE_GRACE_DAYS
        except Exception:
            return False

    # ── Private ──

    def _sync(self, machine_id: str) -> bool:
        import urllib.request

        try:
            payload = json.dumps({"machine_id": machine_id}).encode()
            req = urllib.request.Request(
                f"{_SERVER_URL}/api/v2/sync",
                data=payload,
                headers={"Content-Type": "application/json", "User-Agent": "MuraveyChina/2.0"},
                method="POST",
            )
            with urllib.request.urlopen(req, timeout=10) as resp:
                data = json.loads(resp.read())

            self._data.update({
                "status": data.get("status", self._data.get("status")),
                "free_cutoff_date": data.get("free_cutoff_date", self._data.get("free_cutoff_date")),
                "paid_access_until": data.get("paid_access_until", self._data.get("paid_access_until")),
                "referral_code": data.get("referral_code", self._data.get("referral_code")),
                "referral_balance": data.get("referral_balance", self._data.get("referral_balance", 0)),
                "pricing": data.get("pricing", self._data.get("pricing")),
                "last_sync": datetime.now(timezone.utc).isoformat(),
            })
            self._save_cache()
            log.info("License v2: synced — status=%s", self._data.get("status"))
            return True
        except Exception as exc:
            log.warning("License v2: sync failed — %s (using cached data)", exc)
            return False

    def _load_cache(self) -> None:
        """Load cached license data with HMAC integrity check."""
        try:
            if not self._cache_path.exists():
                return
            raw = self._cache_path.read_text(encoding="utf-8")
            wrapper = json.loads(raw)
            data_str = wrapper.get("data", "{}")
            sig = wrapper.get("sig", "")

            # Verify HMAC
            expected = hmac.new(_HMAC_KEY, data_str.encode(), hashlib.sha256).hexdigest()[:32]
            if not hmac.compare_digest(sig, expected):
                log.warning("License v2: cache signature invalid — ignoring")
                return

            self._data = json.loads(data_str)
        except Exception as exc:
            log.warning("License v2: cache load failed — %s", exc)

    def _save_cache(self) -> None:
        """Save license data with HMAC integrity protection."""
        try:
            data_str = json.dumps(self._data, sort_keys=True)
            sig = hmac.new(_HMAC_KEY, data_str.encode(), hashlib.sha256).hexdigest()[:32]
            wrapper = json.dumps({"data": data_str, "sig": sig})
            self._cache_path.write_text(wrapper, encoding="utf-8")
        except Exception as exc:
            log.warning("License v2: cache save failed — %s", exc)
