"""Read-only public API key management + auth. Keys are generated with full
entropy (secrets.token_urlsafe), so — unlike user passwords — a fast,
deterministic SHA-256 hash is the right storage/lookup choice here: it lets
every request resolve its key with one indexed equality query instead of
iterating every active key and running a slow salted check against each."""

import hashlib
import secrets
from functools import wraps

from django.core.cache import cache
from django.http import JsonResponse
from django.utils import timezone

from accounts.supabase_client import get_client

def _hash_key(raw_key):
    return hashlib.sha256(raw_key.encode('utf-8')).hexdigest()


def generate_api_key(owner_user_id, label=''):
    """Returns the plaintext key — shown to the caller exactly once, never
    stored or retrievable again, only its hash is kept."""
    raw_key = 'pk_' + secrets.token_urlsafe(32)
    get_client().table('pickker_api_keys').insert({
        'owner_user_id': owner_user_id,
        'key_hash': _hash_key(raw_key),
        'label': (label or '').strip(),
        'is_active': True,
    }).execute()
    return raw_key


def list_api_keys(owner_user_id):
    resp = get_client().table('pickker_api_keys').select('id, label, is_active, created_at, last_used_at').eq('owner_user_id', owner_user_id).order('created_at', desc=True).execute()
    return resp.data


def revoke_api_key(key_id, owner_user_id):
    get_client().table('pickker_api_keys').update({'is_active': False}).eq('id', key_id).eq('owner_user_id', owner_user_id).execute()


def _resolve_key(raw_key):
    resp = get_client().table('pickker_api_keys').select('*').eq('key_hash', _hash_key(raw_key)).eq('is_active', True).execute()
    return resp.data[0] if resp.data else None


def _rate_limited(key_id, limit_per_minute):
    cache_key = f'pickker_api_rate_{key_id}'
    count = cache.get(cache_key, 0)
    if count >= limit_per_minute:
        return True
    cache.set(cache_key, count + 1, 60)
    return False


# --- Subscription / license — the gate that sits in front of every key ------
# A valid key alone no longer grants access: the key's owner must also have
# an active, admin-granted subscription (see pickker_api_subscriptions).
# Requesting one is self-service (harmless — it's just a request row);
# activating one is admin-only, using the same manual "we confirmed
# payment, now flip it on" pattern already used for shop/vendor payments
# elsewhere in this app, deliberately not a new payment gateway.

def get_subscription(owner_user_id):
    resp = get_client().table('pickker_api_subscriptions').select('*').eq('owner_user_id', owner_user_id).execute()
    return resp.data[0] if resp.data else None


def request_subscription(owner_user_id, plan='basic'):
    """No-op if a subscription row already exists for this owner (whatever
    its status) — one request per owner, admin sees it once."""
    if get_subscription(owner_user_id):
        return
    get_client().table('pickker_api_subscriptions').insert({
        'owner_user_id': owner_user_id, 'plan': plan, 'status': 'requested',
    }).execute()


def list_all_subscriptions():
    resp = get_client().table('pickker_api_subscriptions').select('*').order('requested_at', desc=True).execute()
    subs = resp.data
    if not subs:
        return subs
    owner_ids = [s['owner_user_id'] for s in subs]
    users_by_id = {u['id']: u for u in get_client().table('pickker_users').select('id, first_name, last_name, email').in_('id', owner_ids).execute().data}
    for s in subs:
        owner = users_by_id.get(s['owner_user_id'], {})
        s['owner_name'] = f"{owner.get('first_name', '')} {owner.get('last_name', '')}".strip() or owner.get('email', '')
        s['owner_email'] = owner.get('email', '')
    return subs


_PLAN_RATE_LIMITS = {'basic': 60, 'pro': 300}


def activate_subscription(subscription_id, admin_user_id, plan=None):
    row = {
        'status': 'active',
        'activated_at': timezone.now().isoformat(),
        'activated_by_admin_id': admin_user_id,
    }
    if plan:
        row['plan'] = plan
        row['rate_limit_per_minute'] = _PLAN_RATE_LIMITS.get(plan, 60)
    get_client().table('pickker_api_subscriptions').update(row).eq('id', subscription_id).execute()


def suspend_subscription(subscription_id):
    get_client().table('pickker_api_subscriptions').update({'status': 'suspended'}).eq('id', subscription_id).execute()


def require_api_key(view_func):
    """Attaches request.api_owner_user_id when a valid, active X-API-Key
    header resolves AND that owner has an active subscription; 401s
    otherwise. 429s past a light per-key rate limit (from the owner's own
    subscription plan, not a fixed constant). No session/CSRF involved —
    this is a separate, key-only auth path for third-party callers,
    distinct from the site's own cookie auth."""
    @wraps(view_func)
    def wrapped(request, *args, **kwargs):
        raw_key = request.headers.get('X-API-Key')
        if not raw_key:
            return JsonResponse({'error': 'Missing X-API-Key header'}, status=401)
        key_row = _resolve_key(raw_key)
        if not key_row:
            return JsonResponse({'error': 'Invalid or revoked API key'}, status=401)
        subscription = get_subscription(key_row['owner_user_id'])
        if not subscription or subscription['status'] != 'active':
            return JsonResponse({'error': 'This key has no active API subscription — contact admin to activate one.'}, status=403)
        if _rate_limited(key_row['id'], subscription['rate_limit_per_minute']):
            return JsonResponse({'error': 'Rate limit exceeded'}, status=429)
        get_client().table('pickker_api_keys').update({'last_used_at': timezone.now().isoformat()}).eq('id', key_row['id']).execute()
        request.api_owner_user_id = key_row['owner_user_id']
        return view_func(request, *args, **kwargs)
    return wrapped
