"""Public structural checks. This is not a semantic decision oracle."""
from pathlib import Path
import argparse, hashlib, json, sys,re
from datetime import date

HERE = Path(__file__).resolve().parent

def pointer(document, ref):
    if not isinstance(ref, str) or not ref.startswith('/'):
        raise ValueError('Evidence references must be JSON Pointers starting with /.')
    value = document
    for token in ref[1:].split('/'):
        if re.search(r'~(?![01])',token):raise ValueError('Invalid JSON Pointer escape.')
        token = token.replace('~1', '/').replace('~0', '~')
        if isinstance(value,list) and not re.fullmatch(r'0|[1-9][0-9]*',token):raise ValueError('Invalid array index.')
        value = value[int(token)] if isinstance(value, list) else value[token]
    return value

def check(fixture_path, submission):
    raw = Path(fixture_path).read_bytes()
    fixture = json.loads(raw)
    errors = []
    if not isinstance(submission,dict):return ['Submission must be an object.']
    bindings = {'fixture_id': fixture['fixture_id'], 'fixture_sha256': hashlib.sha256(raw).hexdigest(),
                'context_id': fixture['context_id'], 'scope': 'SYNTHETIC_ONLY',
                'policy_sha256':hashlib.sha256((HERE/'policy.json').read_bytes()).hexdigest(),
                'contracts_sha256':hashlib.sha256((HERE/'contracts.json').read_bytes()).hexdigest()}
    policy=json.loads((HERE/'policy.json').read_text())
    if bindings['contracts_sha256']!=policy['contracts_sha256']:errors.append('Public contract source differs from the policy binding.')
    for key, expected in bindings.items():
        if submission.get(key) != expected: errors.append('Input binding mismatch: '+key)
    try:
        value=submission.get('evaluated_on')
        if not isinstance(value,str) or date.fromisoformat(value).isoformat()!=value:raise ValueError()
    except (TypeError,ValueError):errors.append('Supply evaluated_on as YYYY-MM-DD.')
    if submission.get('readiness_state') not in ['HOLD','VERIFIED_SYNTHETIC']:
        errors.append('Supply HOLD or VERIFIED_SYNTHETIC.')
    rules = {row[0]: row[1] for row in json.loads((HERE/'policy.json').read_text())['rules']}
    issues = submission.get('issues')
    if not isinstance(issues, list):
        errors.append('issues must be an array.'); issues = []
    seen = set()
    for issue in issues:
        if not isinstance(issue, dict): errors.append('Each issue must be an object.'); continue
        code = issue.get('code')
        if not isinstance(code,str):errors.append('Issue code must be a string.');continue
        if code not in rules or issue.get('state') != rules.get(code): errors.append('Unknown issue code/state pair.')
        if code in seen: errors.append('Duplicate issue code.')
        seen.add(code)
    if submission.get('readiness_state') == 'VERIFIED_SYNTHETIC' and issues:
        errors.append('A readiness decision cannot retain blocking issues.')
    if submission.get('readiness_state') == 'HOLD' and not issues:
        errors.append('A hold needs at least one explicit blocking cause.')
    refs = submission.get('evidence_refs', [])
    if not isinstance(refs, list) or not refs: errors.append('Provide evidence_refs.'); refs = []
    for ref in refs:
        try: pointer(fixture, ref)
        except (ValueError, KeyError, IndexError, TypeError): errors.append('Unresolved evidence reference.')
    for key in ['rationale','minimal_repair','review_trigger']:
        if not isinstance(submission.get(key), str) or not submission[key].strip(): errors.append('Explain '+key)
    return errors

def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--as-of',default=None,help='Explicit historical exercise date for --template; otherwise today is recorded.')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--inventory', action='store_true')
    group.add_argument('--template', nargs=2, metavar=('FIXTURE','OUTPUT'))
    group.add_argument('--check', nargs=2, metavar=('FIXTURE','SUBMISSION'))
    args = parser.parse_args()
    if args.as_of is not None and not args.template:raise ValueError('--as-of is only valid with --template; --check reads evaluated_on from the submission.')
    if args.inventory:
        for path in sorted((HERE/'fixtures').glob('*.json')):
            data=json.loads(path.read_text()); print(path.name, data['evidence_kind'], data['context_id'])
        return 0
    if args.template:
        source, target = map(Path, args.template)
        if target.exists(): raise ValueError('Output exists; choose a new filename to preserve your work.')
        data=json.loads(source.read_text()); result=json.loads((HERE/'submission.template.json').read_text())
        result.update(fixture_id=data['fixture_id'], context_id=data['context_id'],
                      fixture_sha256=hashlib.sha256(source.read_bytes()).hexdigest(),
                      policy_sha256=hashlib.sha256((HERE/'policy.json').read_bytes()).hexdigest(),
                      contracts_sha256=hashlib.sha256((HERE/'contracts.json').read_bytes()).hexdigest(),
                      evaluated_on=date.fromisoformat(args.as_of).isoformat() if args.as_of else date.today().isoformat())
        target.write_text(json.dumps(result, ensure_ascii=False, indent=2)+'\n')
        print('Template created. Decision and reasoning remain yours.'); return 0
    source, target = map(Path, args.check)
    errors=check(source,json.loads(target.read_text()))
    print(json.dumps({'status':'STRUCTURE_PASS_SEMANTICS_UNREVIEWED' if not errors else 'INCOMPLETE',
                      'errors':errors},ensure_ascii=False,indent=2))
    return 1 if errors else 0

if __name__ == '__main__':
    try: raise SystemExit(main())
    except (OSError, ValueError, KeyError, TypeError) as error:
        print('Input error: '+str(error), file=sys.stderr); raise SystemExit(2)
