#!/usr/bin/env python3
"""Public scientific reads without an application run (Python standard library).

Use curve_application.py with a Workspace binding to calculate/publish each link.
This reader discovers the resulting ordinary asset and verifies its exact bytes.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import sys
import urllib.parse
import urllib.request
from application_access import Session

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self,*args,**kwargs):raise ValueError('Redirect refused')


def main():
    p=argparse.ArgumentParser(description=__doc__)
    p.add_argument('--url',required=True);p.add_argument('--project',required=True)
    p.add_argument('--credentials',type=Path);p.add_argument('--asset');p.add_argument('--revision');p.add_argument('--curve',default='GR');p.add_argument('--output',type=Path)
    args=p.parse_args();url=urllib.parse.urlsplit(args.url)
    if url.scheme!='https' and not(url.scheme=='http' and url.hostname in ('localhost','127.0.0.1','::1')):raise ValueError('HTTPS or loopback required')
    if url.username or url.password or url.query or url.fragment:raise ValueError('Plain gateway URL required')
    token=os.environ.get('OPHIOLITE_TOKEN');session=None if token and not args.credentials else Session(args.credentials)
    opener=urllib.request.build_opener(NoRedirect)
    def get(path):
        headers=session.headers(args.url,args.project) if session else {'Authorization':'Bearer '+token}
        with opener.open(urllib.request.Request(args.url.rstrip('/')+path,headers=headers),timeout=60) as r:
            raw=r.read(32*1024*1024+1)
            if len(raw)>32*1024*1024:raise ValueError('Response exceeds 32 MiB')
            return raw
    quote=lambda v:urllib.parse.quote(v,safe='')
    prefix='/api/v1/projects/'+quote(args.project)+'/scientific-assets'
    if not args.asset:
        cursor=''
        while True:
            page=json.loads(get(prefix+'?limit=100&cursor='+quote(cursor)))
            for item in page['items']:print(json.dumps(item))
            cursor=page['next_cursor']
            if not cursor:break
        return
    if not args.revision or not args.output:raise ValueError('Exact --revision and new --output directory required')
    exact=prefix+'/'+quote(args.asset)+'/revisions/'+quote(args.revision)
    descriptor=json.loads(get(exact+'?curve='+quote(args.curve)))
    payloads={}
    for rep in descriptor['representations']:
        raw=get(exact+'/representations/'+quote(rep['id'])+'?curve='+quote(args.curve))
        if len(raw)!=rep['bytes'] or hashlib.sha256(raw).hexdigest()!=rep['sha256']:raise ValueError('Representation checksum mismatch')
        payloads['curve.json' if rep['kind']=='normalized' else 'artifact.las']=raw
    os.umask(0o077);args.output.mkdir(parents=True,exist_ok=False)
    (args.output/'descriptor.json').write_text(json.dumps(descriptor,indent=2))
    for name,raw in payloads.items():(args.output/name).write_bytes(raw)
    print('Verified exact asset representations; no application run created:',args.output)

if __name__=='__main__':
    try:main()
    except (ValueError,OSError) as e:print('Scientific read failed: '+str(e),file=sys.stderr);sys.exit(1)
