"""Pure local decision recipe with fictitious EUR amounts. Never calls a PSP.
Scope: one final manual capture, independent seller groups, approved partial fulfilment.
"""
from itertools import permutations

ALLOCATIONS = {'A': 10500, 'B': 8700, 'C': 6800}
AUTHORIZED = sum(ALLOCATIONS.values())


def record(decisions, seller, answer):
    if seller not in ALLOCATIONS or answer not in {'accepted', 'refused'}:
        raise ValueError('unknown seller or answer')
    if decisions[seller] != 'pending' and decisions[seller] != answer:
        raise ValueError('terminal decision cannot be silently replaced')
    decisions[seller] = answer


def plan(decisions, status='requires_capture', captured=0, refunded=0,
         now=100, expires=200, accepted_revision=True, partial_allowed=True):
    if set(decisions) != set(ALLOCATIONS) or not set(decisions.values()) <= {'pending', 'accepted', 'refused'}:
        raise ValueError('incomplete or invalid decision set')
    if any(type(x) is not int for x in (captured, refunded, now, expires)):
        raise ValueError('integer facts required')
    if not 0 <= refunded <= captured <= AUTHORIZED:
        raise ValueError('invalid payment totals')
    if status not in {'requires_capture', 'succeeded', 'canceled', 'unknown'}:
        raise ValueError('unsupported status')
    if status == 'unknown':
        return ('reconcile', 0)
    if not accepted_revision or not partial_allowed:
        return ('buyer-decision', 0)
    if 'pending' in decisions.values():
        return ('wait-seller', 0)
    target = sum(ALLOCATIONS[s] for s in decisions if decisions[s] == 'accepted')
    if status == 'requires_capture':
        if captured or refunded:
            return ('reconcile', 0)  # This recipe excludes multicapture.
        if now >= expires:
            return ('refresh-payment', 0)
        return ('capture-final', target) if target else ('cancel-authorization', 0)
    if status == 'succeeded':
        net = captured - refunded
        if net < target:
            return ('reconcile', 0)
        return ('refund', net - target) if net > target else ('balanced', 0)
    return ('new-payment-decision', 0) if target else ('closed', 0)


def run():
    answers = [('A', 'accepted'), ('B', 'refused'), ('C', 'accepted')]
    for order in permutations(answers):
        decisions = dict.fromkeys(ALLOCATIONS, 'pending')
        assert plan(decisions) == ('wait-seller', 0)
        for seller, answer in order:
            record(decisions, seller, answer)
            record(decisions, seller, answer)  # Duplicate notification.
        assert plan(decisions) == ('capture-final', 17300)
    assert AUTHORIZED == 26000 and AUTHORIZED - 17300 == 8700
    assert plan(decisions, now=200) == ('refresh-payment', 0)
    assert plan(decisions, status='unknown') == ('reconcile', 0)
    assert plan(decisions, accepted_revision=False) == ('buyer-decision', 0)
    assert plan(decisions, partial_allowed=False) == ('buyer-decision', 0)
    assert plan(decisions, status='succeeded', captured=26000) == ('refund', 8700)
    assert plan(decisions, status='succeeded', captured=26000, refunded=8700) == ('balanced', 0)
    assert plan(decisions, status='succeeded', captured=17300) == ('balanced', 0)
    assert plan(dict.fromkeys(ALLOCATIONS, 'refused')) == ('cancel-authorization', 0)
    try:
        record(decisions, 'B', 'accepted')
    except ValueError:
        pass
    else:
        raise AssertionError('late contradictory acceptance was allowed')
    for bad in [-1, True, 26001]:
        try:
            plan(decisions, captured=bad)
        except ValueError:
            pass
        else:
            raise AssertionError('invalid amount allowed')
    # Local intent registry only: no real refund is executed or confirmed.
    intents = {}
    for _ in range(2):
        action = plan(decisions, status='succeeded', captured=26000)
        intents.setdefault(('order-fictive', 'revision-1', action[0]), action[1])
    assert len(intents) == 1
    print('PASS: 6 arrival orders; duplicate seller events; 260 EUR authorized / 173 EUR final capture / 87 EUR excluded; expiry, unknown payment and changed consent block; captured case = 87 EUR refund intention; repeated intention unique; contradictory acceptance and invalid amounts rejected.')


if __name__ == '__main__':
    run()
