# -*- coding: utf-8 -*-
"""Disposable JSON row snapshots; never persist live ListItems or credentials.

The owner must publish persistent row revisions after every relevant committed
state/metadata change, including menu eligibility. Missing revisions disable
caching. Scope includes the library DB, profile, language and calendar date;
account-dependent callers must also supply their non-secret account scope.
Cache writes never touch the library DB or change a row revision.
"""
import json
import os
import time
import zlib

VERSION = 6                              # cards carry the hero fields (2: movies, 3: series, 4: blur, 5: Pro tebe mode, 6: track plural)
MAX_FILES = 128


def _revision(row):
    from wcs.skin.refresh import revisions
    return revisions([row]).get(row)


def key(row, params, scope):
    """None on unavailable revisions/scope: callers fall back to ordinary row construction."""
    try:
        revision = _revision(row)
        if revision is None:
            return None
        # Origin/tokens belong to output decoration, not the stable action URLs.
        config = {k: params.get(k, '') for k in ('q', 'limit', 'sort', 'hw', 'up', 'style', 'click')}
        identity = {'version': VERSION, 'row': row, 'config': config, 'scope': scope, 'revision': revision}
        raw = json.dumps([row, config, scope['db'], scope['profile']], sort_keys=True).encode('utf-8')
        name = '{:08x}{:08x}{:08x}'.format(zlib.crc32(raw), zlib.adler32(raw), zlib.crc32(raw[::-1]))   # light imports
        root = os.path.join(os.path.dirname(scope['db']), 'row_snapshots')
        return os.path.join(root, name + '.json'), identity
    except Exception:
        return None


def read(key):
    if key is None:
        return None
    try:
        with open(key[0], 'r', encoding='utf-8') as handle:
            payload = json.load(handle)
        if payload['identity'] == key[1] and isinstance(payload['items'], list):
            return payload['items']               # [] is a valid successful empty snapshot
    except (OSError, ValueError, KeyError, TypeError):
        pass
    return None


def write(key, items):
    """Atomic unique-temp publication; a racing mutation prevents caching old menus."""
    if key is None:
        return False
    temporary = None
    try:
        path, identity = key
        root = os.path.dirname(path)
        os.makedirs(root, exist_ok=True)
        temporary = os.path.join(root, '{}-{}-{}.tmp'.format(os.path.basename(path), os.getpid(), time.time_ns()))
        with open(temporary, 'w', encoding='utf-8') as handle:
            json.dump({'identity': identity, 'items': items}, handle, ensure_ascii=False, separators=(',', ':'))
        if _revision(identity['row']) != identity['revision']:
            return False
        os.replace(temporary, path)
        temporary = None
        # Bound search/config variants without scanning on warm invocations.
        files = sorted((e for e in os.scandir(root) if e.name.endswith('.json')), key=lambda e: e.stat().st_mtime)
        for entry in files[:-MAX_FILES]:
            os.remove(entry.path)
        return True
    except Exception:
        return False
    finally:
        if temporary:
            try:
                os.remove(temporary)
            except OSError:
                pass
