"""Laboratoire local de décision RTO, sans appel Amazon ni adresse réelle.
Les métadonnées de contrôle sont locales. READY_* autorise un essai, pas une expédition.
Les contrôles n'imitent pas le validateur d'adresses Amazon et n'établissent pas le routage.
"""
from copy import deepcopy
from itertools import permutations
import json

HUB=dict(addressLine1='SITE FICTIF CENTRAL',postalCode='00000',city='Ville fictive',countryCode='FR')
ORIGIN=dict(addressLine1='SITE FICTIF DEPART',postalCode='00000',city='Ville fictive',countryCode='FR')

def sample(key):
    return dict(id=key,account='seller-A',origin='wh-A',origin_country='FR',
        rollout_confirmed_for_pilot=True,default=dict(account='seller-A',origin='wh-A',version=3,approved=True),
        default_version_at_review=3,requested=deepcopy(HUB),commercial_review=True,
        explicit_default=False,wire_getRates=deepcopy(HUB),wire_oneClickShipment=deepcopy(HUB),
        http=200,label_has_return_address=False)

def qualify(x):
    """Règle métier locale volontairement plus stricte que l'acceptation API."""
    if not x['rollout_confirmed_for_pilot']:return 'HOLD_ACTIVATION'
    d=x['default']
    if (d['account'],d['origin']) != (x['account'],x['origin']):return 'HOLD_DEFAULT_SCOPE'
    if not d['approved'] or d['version']!=x['default_version_at_review']:return 'HOLD_DEFAULT'
    wanted=x['requested']
    if wanted is None:
        if x['wire_getRates'] is not None or x['wire_oneClickShipment'] is not None:return 'HOLD_UNEXPECTED_ADDRESS'
        return 'READY_DEFAULT_PILOT' if x['explicit_default'] else 'HOLD_DEFAULT_NOT_CHOSEN'
    if not all(isinstance(wanted.get(k),str) and wanted[k].strip() for k in ['addressLine1','postalCode','countryCode']):return 'HOLD_INCOMPLETE'
    if wanted['countryCode']!=x['origin_country']:return 'HOLD_COUNTRY'
    if x['commercial_review'] is not True:return 'HOLD_COMMERCIAL_REVIEW'
    if x['wire_getRates']!=wanted or x['wire_oneClickShipment']!=wanted:return 'HOLD_PASSTHROUGH'
    return 'READY_REQUESTED_PILOT'

CASES=[sample('E'+str(n)) for n in range(1,9)]
CASES[1]['commercial_review']=False
for key in ['requested','wire_getRates','wire_oneClickShipment']:CASES[2][key]=None
CASES[2]['explicit_default']=True
for key in ['requested','wire_getRates','wire_oneClickShipment']:CASES[3][key]['countryCode']='ES'
CASES[4]['wire_oneClickShipment']=None
CASES[5]['default']['approved']=False
CASES[6]['rollout_confirmed_for_pilot']=False
CASES[7]['default']['account']='seller-B'
EXPECTED=dict(E1='READY_REQUESTED_PILOT',E2='HOLD_COMMERCIAL_REVIEW',E3='READY_DEFAULT_PILOT',E4='HOLD_COUNTRY',E5='HOLD_PASSTHROUGH',E6='HOLD_DEFAULT',E7='HOLD_ACTIVATION',E8='HOLD_DEFAULT_SCOPE')

def main():
    checks=0
    def check(c):
        nonlocal checks
        assert c;checks+=1
    got={x['id']:qualify(x) for x in CASES};check(got==EXPECTED)
    before=deepcopy(CASES)
    for order in permutations(CASES[:4]):check({x['id']:qualify(x) for x in order}=={k:EXPECTED[k] for k in ['E1','E2','E3','E4']})
    check(CASES==before)
    check(sum(v.startswith('READY') for v in got.values())==2)
    check(sum(v.startswith('HOLD') for v in got.values())==6)
    # Trois raccourcis incorrects : tous les HTTP200 ; toutes les étiquettes ; shipFrom=returnTo.
    check(sum(x['http']==200 for x in CASES)==8)
    check(sum(x['label_has_return_address'] for x in CASES)==0)
    check(ORIGIN!=CASES[0]['requested'])
    # Variantes de recette indépendantes du résultat nominal.
    variants=[]
    def variant(name,modify,expected):
        x=sample('T');modify(x);check(qualify(x)==expected);variants.append(name)
    variant('défaut révisé après revue',lambda x:x['default'].update(version=4),'HOLD_DEFAULT')
    variant('autre entrepôt même compte',lambda x:x['default'].update(origin='wh-B'),'HOLD_DEFAULT_SCOPE')
    variant('voie manquante',lambda x:x['requested'].pop('addressLine1'),'HOLD_INCOMPLETE')
    variant('code postal vide',lambda x:x['requested'].update(postalCode=' '),'HOLD_INCOMPLETE')
    variant('caractère commercial inconnu',lambda x:x.update(commercial_review=None),'HOLD_COMMERCIAL_REVIEW')
    variant('getRates seulement incorrect',lambda x:x.update(wire_getRates=deepcopy(ORIGIN)),'HOLD_PASSTHROUGH')
    variant('oneClick seulement incorrect',lambda x:x.update(wire_oneClickShipment=deepcopy(ORIGIN)),'HOLD_PASSTHROUGH')
    x=sample('T');x.update(requested=None,wire_getRates=None,wire_oneClickShipment=None)
    check(qualify(x)=='HOLD_DEFAULT_NOT_CHOSEN');variants.append('défaut non choisi')
    x['explicit_default']=True;x['wire_oneClickShipment']=HUB
    check(qualify(x)=='HOLD_UNEXPECTED_ADDRESS');variants.append('adresse injectée malgré défaut choisi')
    # Statut HTTP et présence sur étiquette ne changent jamais le verdict de préparation.
    for x in CASES:
        y=deepcopy(x);y.update(http=201,label_has_return_address=True);check(qualify(y)==qualify(x))
    check(12*18==216) # heures opérateur fictives : 12 colis *18 min =216min=3h36.
    print(json.dumps(dict(checks=checks,cases=got,variants=variants,readyForPilot=2,held=6,actualRouting='UNKNOWN for all: no carrier execution',witnesses=dict(http200WouldAccept=8,labelWouldAccept=0,originAlwaysWrongFor='E1'),hypotheticalRehandlingMinutes=216),ensure_ascii=False,indent=2))
if __name__=='__main__':main()
