"""仅监听环回地址的合成故障服务，不连接真实平台。"""
import argparse
from collections import Counter
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import threading
from urllib.request import Request, urlopen
from urllib.error import HTTPError


class Fixture(BaseHTTPRequestHandler):
    counts = Counter()
    lock = threading.Lock()

    def log_message(self, *args):
        pass

    def send_json(self, status, value):
        data = json.dumps(value).encode()
        self.send_response(status)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Content-Length', str(len(data)))
        if status == 429:
            self.send_header('Retry-After', '1')
        self.end_headers()
        self.wfile.write(data)

    def do_GET(self):
        if self.path != '/stats':
            self.send_json(404, {'error': 'unknown path'})
            return
        with self.lock:
            snapshot = dict(self.counts)
        self.send_json(200, snapshot)

    def do_POST(self):
        size = int(self.headers.get('Content-Length', '0'))
        if size > 1024 * 1024:
            self.send_json(413, {'error': 'fixture payload too large'})
            self.close_connection = True
            return
        self.rfile.read(size)
        job = self.headers.get('X-Test-Job', 'demo')
        key = self.path + '|' + job
        with self.lock:
            self.counts[key] += 1
            count = self.counts[key]
        if self.path == '/rate':
            self.send_json(429 if count <= 2 else 200,
                           {'state': 'limited' if count <= 2 else 'complete'})
        elif self.path == '/ok':
            self.send_json(200, {'state': 'complete', 'text': 'synthetic'})
        elif self.path == '/bad':
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"state":')
        elif self.path == '/cut':
            self.send_response(200)
            self.send_header('Content-Type', 'text/event-stream')
            self.end_headers()
            self.wfile.write(b'data: {"text":"partial"}\n\n')
            self.wfile.flush()
            self.close_connection = True
        else:
            self.send_json(404, {'error': 'unknown path'})


def self_test():
    Fixture.counts.clear()
    server = ThreadingHTTPServer(('127.0.0.1', 0), Fixture)
    thread = threading.Thread(target=server.serve_forever, daemon=True)
    thread.start()
    base = 'http://127.0.0.1:' + str(server.server_port)

    def call(path):
        request = Request(base + path, data=b'{}',
                          headers={'X-Test-Job': 'self-test'})
        try:
            response = urlopen(request, timeout=3)
        except HTTPError as exc:
            response = exc
        with response:
            return response.status, response.read()
    try:
        assert [call('/rate')[0] for _ in range(3)] == [429, 429, 200]
        status, body = call('/cut')
        assert status == 200 and b'partial' in body and b'[DONE]' not in body
        status, body = call('/bad')
        try:
            json.loads(body)
        except json.JSONDecodeError:
            pass
        else:
            raise AssertionError('坏JSON样本未触发解析失败')
        assert call('/ok')[0] == 200
        with urlopen(base + '/stats', timeout=3) as response:
            counts = json.load(response)
        assert counts == {'/rate|self-test': 3, '/cut|self-test': 1,
                          '/bad|self-test': 1, '/ok|self-test': 1}
        print(json.dumps({'状态': '夹具自检通过', '请求计数': counts}, ensure_ascii=False))
    finally:
        server.shutdown()
        server.server_close()
        thread.join(timeout=3)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--port', type=int, default=8765)
    parser.add_argument('--self-test', action='store_true')
    args = parser.parse_args()
    if args.self_test:
        self_test()
    else:
        with ThreadingHTTPServer(('127.0.0.1', args.port), Fixture) as server:
            print('仅用于本地合成测试，按Ctrl+C停止：127.0.0.1:' + str(args.port))
            try:
                server.serve_forever()
            except KeyboardInterrupt:
                pass
