"""Read-only publication inventory check. Authenticate RELEASE-MANIFEST.json first."""
import hashlib
import json
import re
import stat
import sys
from pathlib import Path, PurePosixPath
ROOT = Path(__file__).resolve().parent

def verify():
    def pairs(items):
        result = {}
        for k, v in items:
            if k in result:
                raise ValueError('duplicate JSON key')
            result[k] = v
        return result
    manifest = ROOT / 'RELEASE-MANIFEST.json'
    if manifest.is_symlink() or not manifest.is_file():
        raise ValueError('manifest must be a regular file')
    data = manifest.read_bytes()
    m = json.loads(data, object_pairs_hook=pairs)
    if (m['package_id'], m['packaging_revision'], m['kind']) != (
        'EZA-001-DEVELOPER-TRIAL-001', 'online-distribution-001',
        'EZA-publication-manifest/1'):
        raise ValueError('unexpected publication identity')
    expected = {}
    for r in m['files']:
        n = r['path']
        p = PurePosixPath(n)
        if (not n or p.is_absolute() or '..' in p.parts or str(p) != n
            or '\\' in n or n in expected
            or not re.fullmatch('[0-9a-f]{64}', r['sha256'])
            or type(r['bytes']) is not int or r['bytes'] < 0):
            raise ValueError('invalid inventory entry')
        expected[n] = r
    optional = {'RELEASE-MANIFEST.json.minisig'}
    required = {'RELEASE-MANIFEST.json', 'RELEASE-MANIFEST.json.sha256'}
    if (optional | required) & set(expected):
        raise ValueError('reserved inventory path')
    actual = set()
    for p in ROOT.rglob('*'):
        mode = p.lstat().st_mode
        if stat.S_ISDIR(mode):
            continue
        if not stat.S_ISREG(mode):
            raise ValueError('linked or nonregular path')
        n = p.relative_to(ROOT).as_posix()
        actual.add(n)
        if n in expected:
            b = p.read_bytes()
            if len(b) != expected[n]['bytes'] or hashlib.sha256(b).hexdigest() != expected[n]['sha256']:
                raise ValueError('changed file: ' + n)
    if not required <= actual or actual - optional != set(expected) | required:
        raise ValueError('missing/extra publication files')
    pin = hashlib.sha256(data).hexdigest()
    if (ROOT / 'RELEASE-MANIFEST.json.sha256').read_text() != pin + '  RELEASE-MANIFEST.json\n':
        raise ValueError('manifest digest companion mismatch')
    sums = (ROOT / 'SHA256SUMS').read_bytes()
    if hashlib.sha256(sums).hexdigest() != m['checksum_inventory']['sha256']:
        raise ValueError('checksum inventory mismatch')
    lines = ''.join(expected[n]['sha256'] + '  ' + n + '\n' for n in sorted(expected) if n != 'SHA256SUMS')
    if sums != lines.encode():
        raise ValueError('checksum entries mismatch')
    return {'status': 'PASS', 'manifest_sha256': pin,
            'payload_files': len(expected), 'publisher_signature_checked': False,
            'installation_or_runtime_executed': False}

if __name__ == '__main__':
    try:
        print(json.dumps(verify(), indent=2))
    except Exception as e:
        print(json.dumps({'status': 'FAIL', 'error': str(e)}))
        sys.exit(1)
