"""Licenciamento online com cache local e grace period de 24h offline.

- `activate(key)` fala com o servidor, recebe token assinado (TTL 24h),
  grava em `data/license.json`.
- `ensure_valid()` é o gate: chamado antes de cada operação sensível.
  Revalida online a cada 6h; se offline, aceita o token até o exp.
- Token expirou OU servidor disse revogado → sistema trava.
"""
import json
import os
import ssl
import time
import threading
import urllib.request

SERVER = os.environ.get('IRONDOME_LICENSE_SERVER', 'https://irondome-site.vercel.app')
APP_DIR = os.path.dirname(os.path.abspath(__file__))
LIC_PATH = os.path.join(APP_DIR, 'data', 'license.json')

_lock = threading.Lock()
_cache = None  # dict loaded from disk


def _load():
    global _cache
    if _cache is not None:
        return _cache
    try:
        if os.path.exists(LIC_PATH):
            with open(LIC_PATH, 'r', encoding='utf-8') as f:
                _cache = json.load(f)
        else:
            _cache = {}
    except Exception:
        _cache = {}
    return _cache


def _save():
    try:
        os.makedirs(os.path.dirname(LIC_PATH), exist_ok=True)
        with open(LIC_PATH, 'w', encoding='utf-8') as f:
            json.dump(_cache or {}, f, indent=2)
    except Exception:
        pass


def _post(path, payload, timeout=6):
    url = SERVER.rstrip('/') + path
    data = json.dumps(payload).encode()
    req = urllib.request.Request(url, data=data, method='POST')
    req.add_header('Content-Type', 'application/json')
    ctx = ssl.create_default_context()
    with urllib.request.urlopen(req, context=ctx, timeout=timeout) as r:
        return json.loads(r.read())


def activate(key: str) -> dict:
    """Chamado pela UI ao inserir a key. Valida online, grava token."""
    global _cache
    try:
        r = _post('/api/license/verify', {'key': key})
    except Exception as e:
        return {'ok': False, 'reason': f'falha de rede: {e}'}
    if not r.get('ok'):
        return r
    with _lock:
        _cache = {
            'key': key,
            'owner_id': r.get('owner_id'),
            'expires_at': r.get('expires_at'),     # expiração da licença (unix)
            'token': r.get('token'),
            'token_issued_at': int(time.time()),
            'token_ttl_s': int(r.get('token_ttl_s', 24 * 3600)),
            'last_verified_at': int(time.time()),
        }
        _save()
    return {'ok': True, 'expires_at': r.get('expires_at')}


def _refresh_async():
    """Tenta renovar o token com a key armazenada. Não bloqueia o caller."""
    def _do():
        cache = _load()
        key = cache.get('key')
        if not key:
            return
        try:
            r = _post('/api/license/verify', {'key': key})
        except Exception:
            return  # offline, usa cache existente
        with _lock:
            if r.get('ok'):
                cache['token'] = r['token']
                cache['token_issued_at'] = int(time.time())
                cache['token_ttl_s'] = int(r.get('token_ttl_s', 24 * 3600))
                cache['expires_at'] = r.get('expires_at')
                cache['last_verified_at'] = int(time.time())
                _save()
            elif r.get('reason') in ('licença revogada', 'licença expirada', 'assinatura inválida', 'formato inválido'):
                # Servidor recusou explicitamente: invalida token local
                cache['revoked_reason'] = r.get('reason')
                cache['token'] = None
                _save()
    threading.Thread(target=_do, daemon=True).start()


def status() -> dict:
    """Retorna estado atual da licença: licensed, reason, expires_at, etc."""
    c = _load()
    key = c.get('key')
    if not key:
        return {'licensed': False, 'reason': 'não ativada', 'needs_activation': True}
    if c.get('revoked_reason'):
        return {'licensed': False, 'reason': c['revoked_reason'], 'needs_activation': False}
    issued = int(c.get('token_issued_at', 0) or 0)
    ttl = int(c.get('token_ttl_s', 24 * 3600) or 24 * 3600)
    token_exp = issued + ttl
    now = int(time.time())
    # Se token ainda tem validade, ok.
    if c.get('token') and now < token_exp:
        # Se passou 6h desde última verificação, tenta renovar em background
        if now - int(c.get('last_verified_at', 0) or 0) > 6 * 3600:
            _refresh_async()
        return {
            'licensed': True,
            'expires_at': c.get('expires_at'),
            'token_expires_at': token_exp,
            'owner_id': c.get('owner_id'),
            'seconds_until_token_exp': max(0, token_exp - now),
        }
    # Token vencido → força revalidação síncrona rápida
    try:
        r = _post('/api/license/verify', {'key': key}, timeout=4)
    except Exception as e:
        return {'licensed': False, 'reason': f'token expirado e servidor inalcançável ({e})'}
    if r.get('ok'):
        with _lock:
            c['token'] = r['token']
            c['token_issued_at'] = int(time.time())
            c['token_ttl_s'] = int(r.get('token_ttl_s', 24 * 3600))
            c['expires_at'] = r.get('expires_at')
            c['last_verified_at'] = int(time.time())
            _save()
        return {'licensed': True, 'expires_at': c.get('expires_at'),
                'token_expires_at': int(time.time()) + int(c['token_ttl_s']),
                'owner_id': c.get('owner_id')}
    # servidor disse não
    with _lock:
        c['revoked_reason'] = r.get('reason', 'licença inválida')
        c['token'] = None
        _save()
    return {'licensed': False, 'reason': c['revoked_reason']}


def is_licensed() -> bool:
    return bool(status().get('licensed'))


def deactivate():
    """Apaga licença local (debug)."""
    global _cache
    with _lock:
        _cache = {}
        if os.path.exists(LIC_PATH):
            os.remove(LIC_PATH)
