"""Exercice fictif local : orientation de lignes, sans appel Amazon ni transporteur.
Les entrées sont normalisées pour l'exercice, pas des réponses SP-API.
"""
from copy import deepcopy
from itertools import permutations
import json


def classify(row):
    if row['channel'] != 'MERCHANT' or not row['business']:
        return 'HORS_PERIMETRE'
    if not row['complete']:
        return 'A_VERIFIER'
    value = row.get('requirement')
    if value == 'MANDATORY':
        return 'CONFLIT_RECEPTION' if row['capability'] == 'PALLET_DISABLED' else 'PALETTE_REQUISE'
    if value not in (None, ''):
        return 'A_VERIFIER'
    return 'SANS_EXIGENCE_OBSERVEE'


def route(rows):
    result = {}
    for row in rows:
        key = (row['seller'], row['store'], row['order'], row['line'])
        if key in result:
            raise ValueError('Ligne dupliquée à rapprocher avant décision')
        result[key] = classify(row)
    return result


def report_value(headers, values):
    # Alias explicitement acceptés dans cet adaptateur fictif.
    matches = [i for i, name in enumerate(headers) if name in ('pallet-delivery', 'PalletDelivery')]
    if len(headers) != len(values) or len(matches) != 1:
        return False, None
    return True, values[matches[0]]


def retain(previous, incoming):
    """Politique de revue locale : aucun retrait silencieux d'une exigence acquise."""
    if previous in ('PALETTE_REQUISE', 'CONFLIT_RECEPTION') and incoming != previous:
        return {'required': True, 'review': True, 'observed': incoming}
    return {'required': incoming in ('PALETTE_REQUISE', 'CONFLIT_RECEPTION'),
            'review': incoming in ('A_VERIFIER', 'CONFLIT_RECEPTION'), 'observed': incoming}


def sample(order, line='L1', requirement=None, capability='PALLET_ENABLED', complete=True, channel='MERCHANT', business=True):
    return dict(seller='VENDEUR-TEST', store='FR-TEST', order=order, line=line,
                requirement=requirement, capability=capability, complete=complete,
                channel=channel, business=business)


def main():
    checks = 0
    def check(condition):
        nonlocal checks
        assert condition
        checks += 1
    rows = [sample('A', requirement='MANDATORY'), sample('A', 'L2'), sample('B'),
            sample('C', requirement='MANDATORY', capability='PALLET_DISABLED'),
            sample('D', complete=False), sample('E', requirement='MANDATORY', channel='AMAZON'),
            sample('F', requirement='VALEUR_INCONNUE')]
    expected = ['PALETTE_REQUISE', 'SANS_EXIGENCE_OBSERVEE', 'SANS_EXIGENCE_OBSERVEE',
                'CONFLIT_RECEPTION', 'A_VERIFIER', 'HORS_PERIMETRE', 'A_VERIFIER']
    result = route(rows)
    check(list(result.values()) == expected)
    check(len(result) == 7)
    check(list(result.values()).count('PALETTE_REQUISE') == 1)
    check(list(result.values()).count('SANS_EXIGENCE_OBSERVEE') == 2)
    check(sum(x in ('CONFLIT_RECEPTION', 'A_VERIFIER') for x in result.values()) == 3)
    check(list(result.values()).count('HORS_PERIMETRE') == 1)
    for item in permutations(rows[:3]):
        check(route(list(item) + rows[3:]) == result)
    check(classify(sample('X', requirement='MANDATORY', capability=None)) == 'PALETTE_REQUISE')
    check(classify(sample('X', requirement='', capability='PALLET_DISABLED')) == 'SANS_EXIGENCE_OBSERVEE')
    check(classify(sample('X', requirement='MANDATORY', business=False)) == 'HORS_PERIMETRE')
    for bad in [False, 0, [], {}, 'OPTIONAL']:
        check(classify(sample('X', requirement=bad)) == 'A_VERIFIER')
    for headers, values, expected_report in [
        (['order-id', 'pallet-delivery'], ['A', 'MANDATORY'], (True, 'MANDATORY')),
        (['order-id', 'PalletDelivery'], ['A', ''], (True, '')),
        (['order-id'], ['A'], (False, None)),
        (['pallet-delivery', 'PalletDelivery'], ['MANDATORY', ''], (False, None)),
        (['pallet-delivery'], [], (False, None)),
        (['pallet_delivery'], ['MANDATORY'], (False, None)),
    ]:
        check(report_value(headers, values) == expected_report)
    check(retain('PALETTE_REQUISE', 'A_VERIFIER')['required'])
    check(retain('PALETTE_REQUISE', 'SANS_EXIGENCE_OBSERVEE')['review'])
    check(not retain('PALETTE_REQUISE', 'PALETTE_REQUISE')['review'])
    check(retain('CONFLIT_RECEPTION', 'PALETTE_REQUISE')['review'])
    other = deepcopy(rows[0]); other['seller'] = 'AUTRE-VENDEUR'
    check(len(route(rows + [other])) == 8)
    try:
        route(rows + [rows[0]])
    except ValueError:
        check(True)
    else:
        raise AssertionError('Le doublon doit arrêter la projection')
    # Trois témoins volontairement insuffisants.
    check(rows[2]['capability'] == 'PALLET_ENABLED' and expected[2] != 'PALETTE_REQUISE')
    check(rows[4].get('requirement') is None and expected[4] != 'SANS_EXIGENCE_OBSERVEE')
    order_level = {'A': any(r['requirement'] == 'MANDATORY' for r in rows if r['order'] == 'A')}
    check(order_level['A'] and expected[1] == 'SANS_EXIGENCE_OBSERVEE')
    print(json.dumps({'checks': checks, 'permutations': 6, 'incorrectWitnesses': 3,
                      'lines': {'/'.join(k[2:]): v for k, v in result.items()},
                      'externalEffects': False}, ensure_ascii=False, indent=2))


if __name__ == '__main__':
    main()
