#!/usr/bin/env python3
"""布局列表框第 9 关测试接口。仅需 Python 3 标准库。

python3 course-server.py --host 0.0.0.0 --port 8765
手机访问 http://电脑局域网IP:8765/products?page=1&size=10
--fail-page 2 模拟第二页失败；--delay 6 模拟超时；--invalid-json 模拟坏 JSON。
"""
import argparse
import json
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlsplit


def products():
    rows = []
    for number in range(1, 31):
        kind = (number - 1) % 3
        name, category, image, price = (
            ('保温杯', '生活', 'cup.png', 1590),
            ('帆布袋', '生活', 'bag.png', 3900),
            ('学习台灯', '学习', 'lamp.png', 5900),
        )[kind]
        rows.append(dict(id='p%02d' % number, name='%s %02d' % (name, number),
                         category=category, image=image, price=price + number * 10,
                         stock=0 if number % 5 == 0 else (number % 8) + 1))
    return rows


class CourseHandler(BaseHTTPRequestHandler):
    def send_body(self, status, body, content_type='application/json; charset=utf-8'):
        self.send_response(status)
        self.send_header('Content-Type', content_type)
        self.send_header('Content-Length', str(len(body)))
        self.send_header('Cache-Control', 'no-store')
        self.end_headers()
        try:
            self.wfile.write(body)
        except (BrokenPipeError, ConnectionResetError):
            # 手机超时关闭连接时，测试服务继续处理后续重试。
            pass

    def send_json(self, status, value):
        self.send_body(status, json.dumps(value, ensure_ascii=False).encode('utf-8'))

    def do_GET(self):
        url = urlsplit(self.path)
        if url.path != '/products':
            self.send_json(404, dict(error='接口不存在，请使用 /products'))
            return
        query = parse_qs(url.query, keep_blank_values=True)
        try:
            page = int(query.get('page', ['1'])[0])
            size = int(query.get('size', ['10'])[0])
            if page < 1 or not 1 <= size <= 100:
                raise ValueError()
        except ValueError:
            self.send_json(400, dict(error='page 必须为正整数，size 必须为 1 到 100'))
            return
        keyword = query.get('q', [''])[0].strip().casefold()
        options = self.server.course_options
        if options.delay:
            time.sleep(options.delay)
        if options.fail_page == page:
            self.send_json(503, dict(error='教学模拟：第 %d 页暂时不可用' % page))
            return
        if options.invalid_json:
            self.send_body(200, b'{broken-json')
            return
        filtered = [row for row in products()
                    if keyword in (row['name'] + row['category']).casefold()]
        start = (page - 1) * size
        self.send_json(200, dict(page=page, items=filtered[start:start + size],
                                 hasMore=start + size < len(filtered), total=len(filtered)))


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--host', default='0.0.0.0', help='监听地址，默认允许同一局域网手机访问')
    parser.add_argument('--port', type=int, default=8765)
    parser.add_argument('--fail-page', type=int, default=0, help='指定页返回 HTTP 503；0 为关闭')
    parser.add_argument('--delay', type=float, default=0, help='每次请求等待的秒数')
    parser.add_argument('--invalid-json', action='store_true', help='返回 HTTP 200 和无法解析的 JSON')
    options = parser.parse_args()
    if options.delay < 0 or options.fail_page < 0 or not 1 <= options.port <= 65535:
        parser.error('delay、fail-page 不可为负，port 必须为 1 到 65535')
    server = ThreadingHTTPServer((options.host, options.port), CourseHandler)
    server.course_options = options
    print('接口已启动：http://%s:%d/products?page=1&size=10' % (options.host, options.port), flush=True)
    print('手机请将地址中的主机替换成电脑的实际局域网 IP；按 Ctrl+C 停止。', flush=True)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()


if __name__ == '__main__':
    main()
