#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""CubePlot ローカルサーバ
dist/ を http://127.0.0.1:<port> で配信する。外部への通信は一切行わない。

使い方:
    python3 serve.py            # 既定ポート(config.json または 8347)
    python3 serve.py --port 9000
    python3 serve.py --open     # 起動後に既定ブラウザで開く
    python3 serve.py --dir ../dist

PowerPoint運用: スライドに http://127.0.0.1:8347/?minimal=1 へのハイパーリンクを
挿入し、発表中にクリックしてブラウザで開く(手順書 docs/powerpoint_embed_ja.md 参照)。
"""
import argparse
import json
import os
import socket
import sys
import webbrowser
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer

DEFAULT_PORT = 8347


def load_config_port(script_dir):
    """serve.py と同じ場所の config.json から port を読む(なければ既定値)"""
    path = os.path.join(script_dir, "config.json")
    try:
        with open(path, encoding="utf-8") as f:
            cfg = json.load(f)
        if not isinstance(cfg, dict):
            raise ValueError("config.json はオブジェクト形式( {\"port\": 8347} )で記述してください")
        port = int(cfg.get("port", DEFAULT_PORT))
        if 1024 <= port <= 65535:
            return port
        print(f"警告: config.json の port ({port}) が範囲外のため {DEFAULT_PORT} を使用します")
    except FileNotFoundError:
        pass
    except (ValueError, TypeError, AttributeError, OSError, json.JSONDecodeError) as e:
        print(f"警告: config.json を読めません ({e})。既定ポート {DEFAULT_PORT} を使用します")
    return DEFAULT_PORT


class Handler(SimpleHTTPRequestHandler):
    default_doc = "index.html"  # main()が実在するファイル名で上書きする

    # キャッシュさせない(更新反映のトラブル防止)+ 最小限のセキュリティヘッダ
    def end_headers(self):
        self.send_header("Cache-Control", "no-store")
        self.send_header("X-Content-Type-Options", "nosniff")
        self.send_header("Referrer-Policy", "no-referrer")
        super().end_headers()

    def do_GET(self):
        # ルートは既定ドキュメントへ(index.html固定というWebの都合を配布物に持ち込まない)
        if self.path == "/" or self.path.startswith("/?"):
            self.path = "/" + self.default_doc + (self.path[1:] if self.path.startswith("/?") else "")
        if self.path == "/health":
            body = b'{"status":"ok","app":"CubePlot"}'
            self.send_response(200)
            self.send_header("Content-Type", "application/json; charset=utf-8")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
            return
        super().do_GET()

    def log_message(self, fmt, *args):  # 静かめのログ
        sys.stderr.write("  %s - %s\n" % (self.address_string(), fmt % args))

    extensions_map = {
        **SimpleHTTPRequestHandler.extensions_map,
        ".csv": "text/csv; charset=utf-8",
        ".js": "text/javascript; charset=utf-8",
        ".html": "text/html; charset=utf-8",
    }


def main():
    script_dir = os.path.dirname(os.path.abspath(__file__))
    parser = argparse.ArgumentParser(description="CubePlot ローカルサーバ")
    parser.add_argument("--port", type=int, default=None, help=f"ポート番号(既定: config.json または {DEFAULT_PORT})")
    parser.add_argument("--dir", default=None, help="配信ディレクトリ(既定: ../dist)")
    parser.add_argument("--open", action="store_true", help="起動後にブラウザで開く")
    args = parser.parse_args()

    port = args.port if args.port else load_config_port(script_dir)
    serve_dir = os.path.abspath(args.dir) if args.dir else os.path.normpath(os.path.join(script_dir, "..", "dist"))

    # 既定ドキュメント: 配布物はCubePlot.html、開発ビルドはindex.html
    default_doc = None
    for cand in ("CubePlot.html", "index.html", "CubePlot_free.html", "cubeplot_free.html"):
        if os.path.isfile(os.path.join(serve_dir, cand)):
            default_doc = cand
            break
    if not default_doc:
        print(f"エラー: {serve_dir} に CubePlot.html(または index.html)がありません。")
        sys.exit(1)
    Handler.default_doc = default_doc

    handler = partial(Handler, directory=serve_dir)
    try:
        # 127.0.0.1 のみにバインド = 同じPC以外からはアクセス不可(データ保護)
        httpd = ThreadingHTTPServer(("127.0.0.1", port), handler)
    except OSError as e:
        if getattr(e, "errno", None) in (48, 98, 10048) or "Address already in use" in str(e):
            print(f"エラー: ポート {port} は使用中です。別のポートを指定してください:")
            print(f"    python3 serve.py --port {port + 1}")
        else:
            print(f"エラー: サーバを起動できません: {e}")
        sys.exit(1)

    url = f"http://127.0.0.1:{port}/"
    print("┌─────────────────────────────────────────────┐")
    print("│  CubePlot ローカルサーバ                    │")
    print("└─────────────────────────────────────────────┘")
    print(f"  URL:        {url}")
    print(f"  配信元:     {serve_dir}")
    print(f"  埋め込み用: {url}?minimal=1")
    print("  ※ このPC以外からはアクセスできません(127.0.0.1のみ)")
    print("  停止: Ctrl+C")
    print()

    if args.open:
        webbrowser.open(url)

    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\n停止しました。")
        httpd.server_close()


if __name__ == "__main__":
    main()
