"""Validate proposed OpenAPI structure/examples, not implementation behavior."""
import json
import copy
from pathlib import Path
from openapi_spec_validator import validate
from openapi_schema_validator import OAS30Validator
from jsonschema import RefResolver

ROOT = Path(__file__).resolve().parent
checks = []
for name in ['store-contract.openapi.json', 'ai-api-contract.openapi.json']:
    document = json.loads((ROOT / name).read_text())
    validate(document)
    count = 0
    for operations in document['paths'].values():
        for method, operation in operations.items():
            if method not in ['get', 'post', 'put', 'delete']:
                continue
            contents = [operation.get('requestBody', {}).get('content', {})]
            contents += [r.get('content', {}) for r in operation['responses'].values()]
            for content in contents:
                for mime, value in content.items():
                    for example in value.get('examples', {}).values():
                        if 'value' not in example:
                            continue
                        payload = copy.deepcopy(example['value'])
                        if mime == 'multipart/form-data' and isinstance(payload, dict):
                            for field, field_schema in value['schema'].get('properties', {}).items():
                                if field_schema.get('format') == 'binary' and isinstance(payload.get(field), str):
                                    payload[field] = payload[field].encode('utf-8')
                        OAS30Validator(value['schema'], resolver=RefResolver.from_schema(document),
                            format_checker=OAS30Validator.FORMAT_CHECKER).validate(payload)
                        count += 1
    checks.append({'file': name, 'spec_valid': True,
        'operations': sum(len(v) for v in document['paths'].values()), 'validated_examples': count})

store = json.loads((ROOT / 'store-contract.openapi.json').read_text())
def invalid(schema, value):
    return not OAS30Validator(schema, resolver=RefResolver.from_schema(store)).is_valid(value)

negative_checks = {
    'empty_nutrient_status_rejected': invalid(store['components']['schemas']['NutrientStatus'], {}),
    'matched_without_composition_rejected': invalid(store['components']['schemas']['Ingredient'], {
        'name': '示例食材', 'weight_g': 100, 'match_status': 'matched',
        'nutrients': {'energy':100,'protein':10,'zinc':1,'magnesium':20}}),
    'negative_nutrient_rejected': invalid(store['components']['schemas']['Nutrients'], {
        'energy':-1,'protein':0,'zinc':None,'magnesium':None}),
}
save = store['paths']['/p/DietPhoto/save']['post']['requestBody']['content']['application/json']
sample = dict(save['examples']['request']['value'], meal_times=4, meal_kind='regular')
negative_checks['night_snack_cannot_be_regular'] = invalid(save['schema'], sample)
sample.update(meal_times=None, meal_kind='extra')
negative_checks['unknown_meal_cannot_be_extra'] = invalid(save['schema'], sample)
assert all(negative_checks.values()), negative_checks
report = {'scope':'Design schema and examples only; no business APIs tested',
    'checks':checks, 'negative_schema_checks':negative_checks}
(ROOT / 'contract-validation.json').write_text(json.dumps(report, ensure_ascii=False, indent=2) + '\n')
print(json.dumps(report, ensure_ascii=False, indent=2))
