"""Deux opérations distinctes : exercice fictif, aucun appel Stripe.
Montants en centimes EUR ; pas de comptabilité générale, taxe ou frais PSP.
Les observations sont des lectures qualifiées, pas des webhooks à appliquer
aveuglément. Le paramètre incertain signale une réponse de reprise perdue.
"""
from dataclasses import dataclass
from copy import deepcopy


@dataclass(frozen=True)
class Dossier:
    order: str = 'C100'
    seller: str = 'A'
    charge: str = 'ch_DEMO'
    transfer: str = 'tr_A_DEMO'
    currency: str = 'eur'
    payment: int = 10000
    transfers: int = 9000
    refund: int = 2000
    commission_credit: int = 200
    opening_cash: int = 5000  # fonds propres disponibles, hors commande


def project(d, refund, reversals, uncertain=False):
    if not (0 < d.refund <= d.payment and 0 <= d.commission_credit <= d.refund):
        raise ValueError('ventilation invalide')
    if (refund['charge'], refund['currency'], refund['amount']) != (d.charge, d.currency, d.refund):
        raise ValueError('remboursement hors périmètre')
    allowed = {'pending', 'requires_action', 'succeeded', 'failed', 'canceled'}
    if refund['status'] not in allowed:
        raise ValueError('statut à qualifier')
    unique = {}
    for r in reversals:
        if (r['transfer'], r['seller'], r['currency']) != (d.transfer, d.seller, d.currency):
            raise ValueError('reprise hors périmètre')
        if type(r['amount']) is not int or r['amount'] <= 0:
            raise ValueError('montant invalide')
        if r['id'] in unique and unique[r['id']] != r:
            raise ValueError('preuve contradictoire')
        unique[r['id']] = r
    recovered = sum(r['amount'] for r in unique.values())
    target = d.refund - d.commission_credit
    if recovered > target:
        raise ValueError('récupération supérieure à la décision')
    if refund['status'] != 'succeeded':
        return {'customer': refund['status'], 'recovered': recovered,
                'outstanding': None, 'action': 'REVIEW_CUSTOMER', 'closed': False}
    remaining = target - recovered
    action = 'RECONCILE_UNKNOWN' if uncertain else ('REVIEW_RECOVERY' if remaining else 'CLOSE')
    contribution = d.payment - d.transfers - d.refund + recovered
    return {'customer': 'succeeded', 'recovered': recovered, 'outstanding': remaining,
            'contribution': contribution, 'cash': d.opening_cash + contribution,
            'action': action, 'closed': not remaining and not uncertain}


def main():
    count = 0
    def check(ok):
        nonlocal count
        assert ok
        count += 1
    def rejects(call):
        try:
            call()
        except ValueError:
            check(True)
        else:
            raise AssertionError('refus attendu')
    d = Dossier()
    refund = {'id': 're_DEMO', 'charge': d.charge, 'currency': 'eur', 'amount': 2000, 'status': 'succeeded'}
    reversal = {'id': 'trr_DEMO', 'transfer': d.transfer, 'seller': 'A', 'currency': 'eur', 'amount': 1800}
    before = project(d, refund, [])
    check(before['customer'] == 'succeeded' and before['outstanding'] == 1800)
    check(before['contribution'] == -1000 and before['cash'] == 4000)
    check(before['action'] == 'REVIEW_RECOVERY' and not before['closed'])
    after = project(d, refund, [reversal])
    check(after['contribution'] == 800 and after['cash'] == 5800)
    check(after['outstanding'] == 0 and after['closed'])
    check(project(d, refund, [reversal, deepcopy(reversal)]) == after)
    check(project(d, refund, [], uncertain=True)['action'] == 'RECONCILE_UNKNOWN')
    check(not project(d, refund, [reversal], uncertain=True)['closed'])
    partial = dict(reversal, amount=1000)
    check(project(d, refund, [partial])['outstanding'] == 800)
    rest = dict(reversal, id='trr_REST', amount=800)
    check(project(d, refund, [partial, rest]) == after)
    for status in ('pending', 'requires_action', 'failed', 'canceled'):
        x = project(d, dict(refund, status=status), [])
        check(not x['closed'] and x['outstanding'] is None and x['action'] == 'REVIEW_CUSTOMER')
    rejects(lambda: project(d, dict(refund, status='new_unknown'), []))
    rejects(lambda: project(d, dict(refund, charge='ch_OTHER'), []))
    rejects(lambda: project(d, dict(refund, currency='usd'), []))
    rejects(lambda: project(d, dict(refund, amount=2100), []))
    rejects(lambda: project(d, refund, [dict(reversal, seller='B')]))
    rejects(lambda: project(d, refund, [dict(reversal, transfer='tr_B_DEMO')]))
    rejects(lambda: project(d, refund, [dict(reversal, currency='usd')]))
    rejects(lambda: project(d, refund, [dict(reversal, amount=2000)]))
    rejects(lambda: project(d, refund, [reversal, dict(reversal, amount=1700)]))
    # Trois témoins incorrects : absence de reprise => faux échec client ;
    # reprise brute de 20 EUR => sur-récupération ; somme des copies => doublon.
    bad_customer = 'failed' if not [] else refund['status']
    check(bad_customer != before['customer'])
    bad_contribution = 10000 - 9000 - 2000 + 2000
    check(bad_contribution == 1000 and bad_contribution != after['contribution'])
    bad_copies = sum(r['amount'] for r in [reversal, reversal])
    check(bad_copies == 3600 and bad_copies != after['recovered'])
    print(f'{count} contrôles réussis ; 3 témoins incorrects détectés')
    print('Après remboursement : contribution -10 EUR, trésorerie 40 EUR, reprise attendue 18 EUR')
    print('Après reprise : contribution 8 EUR, trésorerie 58 EUR, reprise attendue 0 EUR')
    print('Exercice local : aucun remboursement ni transfert exécuté.')


if __name__ == '__main__':
    main()
