"""12 / 14: 承認・期限・重複の模擬演習。
外部への送信やファイル書込みはしない。状態はプロセス内だけ。
本番の認証、永続化、同時実行、配送保証は実装していない。
"""
from dataclasses import dataclass, replace
from hashlib import sha256
import json
import sys


@dataclass(frozen=True)
class Draft:
    request_id: str
    recipient: str
    body: str


@dataclass(frozen=True)
class Approval:
    fingerprint: str
    reviewer: str
    expires_at: int
    granted: bool = True


def fingerprint(draft):
    # ダイジェストは変更検出用。これだけで承認者の本人性は証明できない。
    payload = [draft.request_id, draft.recipient, draft.body]
    return sha256(json.dumps(payload, ensure_ascii=False).encode('utf-8')).hexdigest()


def simulate(draft, approval, now, completed, *, fail=False):
    if not draft.request_id or not draft.recipient or not draft.body:
        return '入力不備'
    # 教材では実在宛先を使用しない。送信処理は存在しない。
    if not draft.recipient.endswith('@example.invalid'):
        return '練習用宛先以外は拒否'
    signature = fingerprint(draft)
    if draft.request_id in completed:
        return '重複除外' if completed[draft.request_id] == signature else '同一IDの内容競合'
    if approval is None:
        return '承認待ち'
    if not approval.granted:
        return '承認拒否'
    if not approval.reviewer:
        return '承認者不明'
    if now >= approval.expires_at:
        return '期限切れ'
    if approval.fingerprint != signature:
        return '内容変更・再承認待ち'
    if fail:
        return '模擬一時障害・未完了'
    completed[draft.request_id] = signature
    return '模擬送信記録のみ'


def self_test():
    d = Draft('R1', 'trainee@example.invalid', '架空の返信')
    a = Approval(fingerprint(d), '研修担当', 200)
    state = {}
    assert simulate(replace(d, recipient=''), a, 100, state) == '入力不備'
    assert simulate(d, None, 100, state) == '承認待ち'
    assert simulate(d, replace(a, granted=False), 100, state) == '承認拒否'
    assert simulate(d, replace(a, reviewer=''), 100, state) == '承認者不明'
    assert simulate(d, a, 200, state) == '期限切れ'
    assert simulate(replace(d, body='変更後'), a, 100, state) == '内容変更・再承認待ち'
    assert simulate(replace(d, recipient='other@example.invalid'), a, 100, state) == '内容変更・再承認待ち'
    assert simulate(d, a, 100, state, fail=True) == '模擬一時障害・未完了'
    assert not state
    assert simulate(d, a, 100, state) == '模擬送信記録のみ'
    assert simulate(d, a, 100, state) == '重複除外'
    assert simulate(replace(d, body='競合'), a, 100, state) == '同一IDの内容競合'
    assert len(state) == 1
    print('PASS: 承認・変更・期限・障害再開・二重実行防止を確認。')


if __name__ == '__main__':
    if '--self-test' in sys.argv:
        self_test()
    else:
        state = {}
        d = Draft('R1', 'trainee@example.invalid', '料金は資料を確認して回答します。')
        a = Approval(fingerprint(d), '研修担当', 200)
        print('未承認:', simulate(d, None, 100, state))
        print('承認後の変更:', simulate(replace(d, body='変更された文面'), a, 100, state))
        print('正常:', simulate(d, a, 100, state))
        print('再実行:', simulate(d, a, 100, state))
        print('模擬記録件数:', len(state))
