#!/usr/bin/env python3
import json, threading, tempfile
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from urllib.error import URLError, HTTPError
from urllib.request import Request, urlopen

class Sink(BaseHTTPRequestHandler):
    def do_POST(self):
        n = int(self.headers.get('Content-Length', '0'))
        self.rfile.read(n)
        self.send_response(204)
        self.end_headers()
    def log_message(self, *_):
        pass

def start_sink():
    server = HTTPServer(('127.0.0.1', 0), Sink)
    t = threading.Thread(target=server.serve_forever, daemon=True)
    t.start()
    return server

def deliver(url, payload):
    try:
        req = Request(url, data=json.dumps(payload).encode(), method='POST',
                      headers={'Content-Type': 'application/json'})
        with urlopen(req, timeout=1) as response:
            return 200 <= response.status < 300
    except (URLError, HTTPError, TimeoutError):
        return False

def run_case(url, should_succeed, state):
    delivered = deliver(url, {'alert': 'test'})
    if delivered:
        state.write_text('reported\n')
    assert delivered is should_succeed, (delivered, should_succeed)
    return delivered

def require_reachable_fixture(url):
    # The fixture must prove its success path before recovery assertions are meaningful.
    probe = deliver(url, {'probe': True})
    assert probe, 'INVALID_FIXTURE: success path is unreachable'

def main():
    with tempfile.TemporaryDirectory() as d:
        state = Path(d) / 'state'
        server = start_sink()
        good = f'http://127.0.0.1:{server.server_port}/hook'
        require_reachable_fixture(good)
        assert run_case(good, True, state) is True
        assert state.read_text() == 'reported\n'
        state.unlink()
        # Inject the failure after validating that the fixture can succeed.
        bad = 'http://127.0.0.1:1/unreachable'
        assert run_case(bad, False, state) is False
        assert not state.exists(), 'failure must not advance state'
        try:
            require_reachable_fixture(bad)
        except AssertionError as e:
            assert str(e) == 'INVALID_FIXTURE: success path is unreachable'
        else:
            raise AssertionError('unreachable fixture was not rejected')
        server.shutdown()
    print('PASS success_reachable state_advances')
    print('PASS injected_failure state_unchanged')
    print('PASS unreachable_fixture rejected_as_invalid')

if __name__ == '__main__':
    main()
