#!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import socket
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path


CSV_FILE_NAME = "bilibili_latest_videos_259655230.csv"
TOKENS_FILE_NAME = "apns_device_tokens.txt"


def is_valid_device_token(token: str) -> bool:
    return len(token) >= 64 and len(token) % 2 == 0 and all(
        character in "0123456789abcdefABCDEF" for character in token
    )


def save_device_token(tokens_path: Path, token: str) -> bool:
    tokens: list[str] = []
    if tokens_path.exists():
        tokens = [
            line.strip()
            for line in tokens_path.read_text(encoding="utf-8").splitlines()
            if line.strip() and not line.lstrip().startswith("#")
        ]

    normalized_token = token.lower()
    if normalized_token in {existing.lower() for existing in tokens}:
        return False

    with tokens_path.open("a", encoding="utf-8") as file:
        if tokens_path.stat().st_size > 0:
            file.write("\n")
        file.write(normalized_token)
    return True


def local_ip_addresses() -> list[str]:
    addresses: set[str] = set()
    host_name = socket.gethostname()

    try:
        for result in socket.getaddrinfo(host_name, None, socket.AF_INET):
            address = result[4][0]
            if not address.startswith("127."):
                addresses.add(address)
    except socket.gaierror:
        pass

    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe:
            probe.connect(("8.8.8.8", 80))
            address = probe.getsockname()[0]
            if not address.startswith("127."):
                addresses.add(address)
    except OSError:
        pass

    return sorted(addresses)


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="启动一个本地 HTTP 服务，向 iOS app 提供 B 站最新视频 CSV"
    )
    parser.add_argument("--host", default="0.0.0.0", help="监听地址，默认 0.0.0.0")
    parser.add_argument("--port", type=int, default=8000, help="监听端口，默认 8000")
    return parser


class CSVServerHandler(SimpleHTTPRequestHandler):
    server_version = "ShaanxiUnionCSV/1.0"

    def end_headers(self) -> None:
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        super().end_headers()

    def do_OPTIONS(self) -> None:
        self.send_response(204)
        self.end_headers()

    def do_POST(self) -> None:
        if self.path != "/register-token":
            self.send_error(404, "Not Found")
            return

        content_length = int(self.headers.get("Content-Length", "0"))
        body = self.rfile.read(content_length)
        try:
            payload = json.loads(body.decode("utf-8"))
            token = str(payload.get("token", "")).strip()
        except (UnicodeDecodeError, json.JSONDecodeError):
            self.send_json(400, {"ok": False, "error": "invalid json"})
            return

        if not is_valid_device_token(token):
            self.send_json(400, {"ok": False, "error": "invalid device token"})
            return

        tokens_path = Path(self.directory) / TOKENS_FILE_NAME
        added = save_device_token(tokens_path, token)
        self.send_json(200, {"ok": True, "added": added})

    def send_json(self, status_code: int, payload: dict[str, object]) -> None:
        data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
        self.send_response(status_code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(data)))
        self.end_headers()
        self.wfile.write(data)


def main() -> None:
    args = build_parser().parse_args()
    root = Path(__file__).resolve().parent
    csv_path = root / CSV_FILE_NAME
    if not csv_path.exists():
        raise SystemExit(f"找不到 CSV 文件：{csv_path}")

    handler = lambda *handler_args: CSVServerHandler(
        *handler_args,
        directory=str(root),
    )
    server = ThreadingHTTPServer((args.host, args.port), handler)

    print("CSV 服务已启动")
    print(f"当前 CSV：{csv_path}")
    print(f"Token 文件：{root / TOKENS_FILE_NAME}")
    print(f"模拟器地址：http://127.0.0.1:{args.port}/{CSV_FILE_NAME}")
    print(f"Token 注册：http://127.0.0.1:{args.port}/register-token")
    for address in local_ip_addresses():
        print(f"真机地址：http://{address}:{args.port}/{CSV_FILE_NAME}")
    print("停止服务：按 Ctrl+C")

    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\nCSV 服务已停止")
    finally:
        server.server_close()


if __name__ == "__main__":
    main()
