#!/usr/bin/env python3
"""Одинаковый воспроизводимый корпус для ядра и JSON Schema 2020-12. Только локальный контроль."""
from pathlib import Path
import copy,hashlib,json,random,subprocess,sys
import jsonschema
R=Path(__file__).resolve().parent.parent
E=R/'доказательства/редакция-4.8.0';E.mkdir(exist_ok=True)
rng=random.Random(460);cases=[]

def sample(s):
 if s is True:return None
 if s is False:return None
 if 'const' in s:return copy.deepcopy(s['const'])
 if 'enum' in s:return copy.deepcopy(s['enum'][0])
 if 'anyOf' in s:return sample(s['anyOf'][0])
 t=s.get('type','object');t=t[0] if isinstance(t,list) else t
 if t=='string':
  if s.get('format')=='date':return '2026-09-01'
  if s.get('format')=='date-time':return '2026-09-01T12:00:00.000Z'
  if s.get('pattern','').startswith('^[a-f0-9]{64}$'):return 'a'*64
  return 'Тест' if s.get('maxLength',4)>=4 else 'я'*s['maxLength']
 if t in ['integer','number']:return s.get('minimum',0)
 if t=='boolean':return False
 if t=='null':return None
 if t=='array':return [sample(s.get('items',True)) for _ in range(s.get('minItems',0))]
 return {k:sample(v) for k,v in s.get('properties',{}).items() if k in s.get('required',[])}

def add(s,v,origin):cases.append({'номер':len(cases)+1,'источник_правила':origin,'схема':s,'вход':v})
probes=[None,False,True,0,1,-1,1.5,'',' ','\u001c','\u0085','\ufeff','\u00a0','\u2003','ё','е\u0308','😀','а\n','abc',[],{},[None],{'неизвестное':'поле'}]

def visit(s,origin):
 if isinstance(s,bool):
  for v in probes:add(s,v,origin)
  return
 valid=sample(s);add(s,valid,origin)
 for v in probes:add(s,v,origin)
 if isinstance(valid,dict):
  for k in s.get('required',[]):
   x=copy.deepcopy(valid);x.pop(k,None);add(s,x,origin+' (пропуск '+k+')')
 if s.get('type')=='array':
  for n in [s.get('minItems',0)-1,s.get('minItems',0),s.get('maxItems',2),s.get('maxItems',2)+1]:
   if 0<=n<=501:add(s,[sample(s.get('items',True))]*n,origin+' (граница '+str(n)+')')
 if s.get('type')=='string':
  for n in set([max(0,s.get('minLength',0)-1),s.get('minLength',0),s.get('maxLength',2),s.get('maxLength',2)+1]):
   if n<=8001:add(s,'я'*n,origin+' (длина '+str(n)+')')
 if s.get('format')=='date':
  for v in ['2024-02-29','2026-02-29','2026-02-30','2026-13-01','0000-01-01','0001-01-01','2026-09-01\n']:add(s,v,origin)
 if s.get('format')=='date-time':
  for v in ['2026-09-01T12:00:00Z','2026-09-01t12:00:00z','2026-09-01T12:00:00+03:00','2026-09-01T24:00:00Z','2026-09-01T12:00:60Z','2026-09-01T12:00:00.123456Z','0000-01-01T00:00:00Z','2026-09-01T12:00:00Z\n']:add(s,v,origin)
 for k,v in s.get('properties',{}).items():visit(v,origin+'.properties.'+k)
 for k in ['items','additionalProperties']:
  if k in s:visit(s[k],origin+'.'+k)
 for i,v in enumerate(s.get('anyOf',[])):visit(v,origin+'.anyOf.'+str(i))

names=['схема-переносимой-записи-аудита','схема-продольной-панели','схема-папки-дела','схема-решения-конфликта','схема-реестра-требований','схема-отзыва-решения','схема-происхождения-переноса','схема-события-исправления']
for name in names:
 p=R/'данные'/(name+'-4.7.0.json');s=json.loads(p.read_text());jsonschema.Draft202012Validator.check_schema(s);visit(s,p.name)
bundle=json.loads((R/'данные/схемы-лаборатории-4.8.0.json').read_text())
for kind in ['drift','pair']:
 s=bundle[kind];jsonschema.Draft202012Validator.check_schema(s);visit(s,'схемы-лаборатории-4.7.0.json:'+kind)
for s in [False,True,{'items':False},{'properties':{'x':False}},{'type':['null','string']},{'type':'array','uniqueItems':True},{'minProperties':1,'maxProperties':2},{'pattern':'^.$'},{'enum':[1,True,{'ё':1}]}]:visit(s,'профиль встроенных правил')
for _ in range(100):
 s={'type':rng.choice(['integer','string','array','object'])};add(s,rng.choice(probes),'генератор, зерно 460')
corpus={'зерно':460,'класс':'ИС — искусственные входы','случаи':cases}
cp=E/'корпус-схем.json';cp.write_text(json.dumps(corpus,ensure_ascii=False,separators=(',',':'))+'\n')
js="const fs=require('fs'),K=require(process.argv[1]);const c=JSON.parse(fs.readFileSync(process.argv[2],'utf8'));process.stdout.write(JSON.stringify(c.случаи.map(x=>{try{return {допущен:K.validate(x.схема,x.вход).length===0,профиль:K.schemaErrors(x.схема)};}catch(e){return {допущен:false,исключение:e.message}}})));"
r=subprocess.run(['node','-e',js,str(R/'ресурсы/сценарии/доказательный-контур.js'),str(cp)],check=True,capture_output=True,text=True)
actual=json.loads(r.stdout);diffs=[];out=[]
for c,a in zip(cases,actual,strict=True):
 validator=jsonschema.Draft202012Validator(c['схема'],format_checker=jsonschema.FormatChecker())
 expected=validator.is_valid(c['вход']);blocked=bool(a.get('профиль'));same=(not a['допущен'] if blocked else expected==a['допущен']) and 'исключение' not in a
 row={'номер':c['номер'],'полный_валидатор':expected,'встроенный':a,'совпало_в_поддерживаемом_профиле':same,'заблокировано_вне_профиля':blocked}
 out.append(row)
 if not same:diffs.append({**c,**row})
report={'редакция':'4.7.0-wip','зерно':460,'проверок':len(cases),'полный_валидатор':'jsonschema '+__import__('importlib.metadata').metadata.version('jsonschema')+' Draft202012Validator + FormatChecker','отпечаток_корпуса':hashlib.sha256(cp.read_bytes()).hexdigest(),'итог':'ПРОЙДЕНО' if not diffs else 'ПРОВАЛЕНО','расхождения':diffs,'случаев_вне_профиля':sum(bool(r['заблокировано_вне_профиля']) for r in out),'результаты':out,'риск':'Молчаливое расхождение встроенного подмножества и независимой реализации на одинаковых входах.','вход':'корпус-схем.json; индивидуальные значения и правила сохранены.','предел':'Конечный генеративный корпус, не доказательство эквивалентности для всех схем. Неподдерживаемые ключевые слова и форматы запрещены. Лексический профиль JSON проверяется отдельно.','команда':'python инструменты/сопоставить-валидаторы.py'}
(E/'сопоставление-валидаторов.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n')
print(json.dumps({'проверок':len(cases),'расхождений':len(diffs),'первые':diffs[:8]},ensure_ascii=False,indent=2));sys.exit(bool(diffs))
