from __future__ import annotations

import argparse
import json
import os
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Any

from src.signals.tradingview import TradingViewWebhookConfig, handle_tradingview_payload
from src.tools.tradingview_webhook import _split_csv


def _load_secret(args: argparse.Namespace) -> str:
    if args.secret:
        return args.secret
    if args.secret_file:
        return Path(args.secret_file).read_text(encoding="utf-8").strip()
    env_secret = os.environ.get("CTB_TRADINGVIEW_WEBHOOK_SECRET")
    if env_secret:
        return env_secret.strip()
    raise SystemExit("missing webhook secret: use --secret-file or CTB_TRADINGVIEW_WEBHOOK_SECRET")


def _config_from_args(args: argparse.Namespace) -> TradingViewWebhookConfig:
    allowed_strategies = _split_csv(args.allowed_strategy, os.environ.get("CTB_TRADINGVIEW_ALLOWED_STRATEGIES"))
    allowed_coins = {coin.upper() for coin in _split_csv(args.allowed_coin, os.environ.get("CTB_TRADINGVIEW_ALLOWED_COINS"))}
    if not allowed_strategies:
        raise SystemExit("missing allowed strategies")
    if not allowed_coins:
        raise SystemExit("missing allowed coins")
    return TradingViewWebhookConfig(
        runtime_dir=Path(args.runtime_dir),
        secret=_load_secret(args),
        allowed_strategies=allowed_strategies,
        allowed_coins=allowed_coins,
    )


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Run a paper-only TradingView webhook receiver.")
    parser.add_argument("--host", default="127.0.0.1")
    parser.add_argument("--port", type=int, default=8765)
    parser.add_argument("--runtime-dir", default="runtime")
    parser.add_argument("--secret")
    parser.add_argument("--secret-file")
    parser.add_argument("--allowed-strategy", action="append", default=[])
    parser.add_argument("--allowed-coin", action="append", default=[])
    return parser


def _send_json(handler: BaseHTTPRequestHandler, status: int, payload: dict[str, Any]) -> None:
    body = json.dumps(payload, sort_keys=True).encode("utf-8")
    handler.send_response(status)
    handler.send_header("Content-Type", "application/json")
    handler.send_header("Content-Length", str(len(body)))
    handler.end_headers()
    handler.wfile.write(body)


def _headers_dict(handler: BaseHTTPRequestHandler) -> dict[str, str]:
    headers: dict[str, str] = {}
    for key, value in handler.headers.items():
        headers[str(key)] = str(value)
        headers[str(key).lower()] = str(value)
    return headers


def make_handler(config: TradingViewWebhookConfig):
    class TradingViewWebhookHandler(BaseHTTPRequestHandler):
        server_version = "CTBTradingViewWebhook/1.0"

        def log_message(self, format: str, *args: Any) -> None:  # keep secrets/payloads out of stdout by default
            return

        def do_GET(self) -> None:  # noqa: N802 - stdlib callback name
            if self.path == "/healthz":
                _send_json(self, 200, {"status": "ok", "mode": "paper_signal", "live_order_allowed": False})
                return
            _send_json(self, 404, {"status": "not_found", "live_order_allowed": False})

        def do_POST(self) -> None:  # noqa: N802 - stdlib callback name
            if self.path != "/webhook/tradingview":
                _send_json(self, 404, {"accepted": False, "reason": "not_found", "live_order_allowed": False})
                return
            length_header = self.headers.get("Content-Length") or "0"
            try:
                length = int(length_header)
            except ValueError:
                _send_json(self, 400, {"accepted": False, "reason": "invalid_content_length", "live_order_allowed": False})
                return
            if length <= 0 or length > 65536:
                _send_json(self, 400, {"accepted": False, "reason": "invalid_body_size", "live_order_allowed": False})
                return
            raw = self.rfile.read(length)
            try:
                payload = json.loads(raw.decode("utf-8"))
            except (UnicodeDecodeError, json.JSONDecodeError):
                _send_json(self, 400, {"accepted": False, "reason": "invalid_json", "live_order_allowed": False})
                return
            if not isinstance(payload, dict):
                _send_json(self, 400, {"accepted": False, "reason": "invalid_payload", "live_order_allowed": False})
                return
            result = handle_tradingview_payload(payload, headers=_headers_dict(self), config=config)
            _send_json(self, 200 if result.get("accepted") else 403, result)

    return TradingViewWebhookHandler


def make_server(args: argparse.Namespace) -> ThreadingHTTPServer:
    config = _config_from_args(args)
    return ThreadingHTTPServer((args.host, args.port), make_handler(config))


def main(argv: list[str] | None = None) -> int:
    args = build_parser().parse_args(argv)
    server = make_server(args)
    host, port = server.server_address[:2]
    print(f"TradingView webhook receiver listening on http://{host}:{port}/webhook/tradingview mode=paper_signal live_order_allowed=false", flush=True)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
