"""Sequential admission-policy fixture, not an AWS/SigV4/HTTP implementation.
Time is synthetic integer seconds. No network, SDK, concurrency or byte transfer.
"""
from dataclasses import dataclass
import json
from pathlib import Path


@dataclass(frozen=True)
class Capability:
    object_id: str
    expires_at: int
    credentials_expire_at: int = 3600


def member_allowed(now, revoked_at=20):
    return now < revoked_at


def storage_admits(capability, now, signer_allowed=True):
    # The model has no app-membership lookup at storage.
    return (now < capability.expires_at
            and now < capability.credentials_expire_at and signer_allowed)


class CachedPermission:
    def __init__(self, ttl):
        self.ttl, self.until, self.value = ttl, -1, False

    def allowed(self, now):
        if now >= self.until:
            self.value = member_allowed(now)
            self.until = now + self.ttl
        return self.value


def run():
    cap = Capability('alpha/export-v1', 120)
    cache = CachedPermission(30)
    traces = []
    for now in range(121):
        direct = storage_admits(cap, now)
        traces.append(dict(second=now, current_member=member_allowed(now),
                           direct_storage_url=direct,
                           broker_retained_target=direct,
                           gateway_cached_permission=cache.allowed(now),
                           gateway_current_permission=member_allowed(now),
                           broker_new_redirect=member_allowed(now)))
    post = [r for r in traces if r['second'] >= 20]
    names = ['direct_storage_url', 'broker_retained_target',
             'gateway_cached_permission', 'gateway_current_permission']
    summary = []
    for name in names:
        allowed = [r['second'] for r in post if r[name]]
        summary.append(dict(path=name, post_revocation_attempts=len(post),
                            admitted=len(allowed), first_admitted=min(allowed) if allowed else None,
                            last_admitted=max(allowed) if allowed else None))
    assert [r['admitted'] for r in summary] == [100, 100, 10, 0]
    assert sum(r['broker_new_redirect'] for r in post) == 0
    by_time = {r['second']: r for r in traces}
    assert by_time[19]['gateway_current_permission'] is True
    assert by_time[20]['gateway_current_permission'] is False
    assert by_time[29]['gateway_cached_permission'] is True
    assert by_time[30]['gateway_cached_permission'] is False
    assert by_time[119]['direct_storage_url'] is True
    assert by_time[120]['direct_storage_url'] is False
    short = Capability('alpha/export-v1', 120, credentials_expire_at=90)
    boundary_checks = [
        dict(case='credentials valid at 89', actual=storage_admits(short, 89), expected=True),
        dict(case='credentials expired at 90', actual=storage_admits(short, 90), expected=False),
        dict(case='signer permission removed', actual=storage_admits(cap, 25, False), expected=False),
        dict(case='repeated use is accepted', actual=storage_admits(cap, 10) and storage_admits(cap, 10), expected=True),
    ]
    assert all(c['actual'] == c['expected'] for c in boundary_checks)
    result = dict(scope='Constructed sequential admission-policy model; no AWS SDK, SigV4, HTTP or byte transfer',
                  assumptions=dict(issued_at=0, app_revoked_at=20, url_expires_at=120,
                                   credentials_expire_at=3600, permission_cache_ttl=30,
                                   first_cached_check_at=0, sampling='one new request per integer second, 0 through 120 inclusive',
                                   signer_policy='unchanged except explicit boundary check', object='fixed alpha/export-v1'),
                  traces=traces, summary=summary,
                  broker_new_redirects_after_revocation=sum(r['broker_new_redirect'] for r in post),
                  boundary_checks=boundary_checks,
                  exclusions=['No claim about real propagation delay', 'No active stream cancellation',
                              'No client or CDN cache implementation', 'No deletion of downloaded bytes',
                              'No authorization-store failure or concurrency model'])
    Path('results.json').write_text(json.dumps(result, indent=2) + '\n')
    print(json.dumps({row['path']: row['admitted'] for row in summary}))


if __name__ == '__main__':
    run()
