from __future__ import annotations

import argparse
import hashlib
import json
import os
import sys
import time
from typing import Any, Dict, Optional

import requests

from .config import XblAuthConfig, DEFAULT_CLIENT_ID
from .orchestrator import Xbl3AuthService


# Remote config endpoint hosted on Cloudflare Workers.
REMOTE_CONFIG_URL = os.getenv(
    "XBL3AUTH_CONFIG_URL",
    "https://xbl3-auth-cf.djstomp.workers.dev",
)


def build_parser() -> argparse.ArgumentParser:
    """Create an argument parser for the xbl3auth CLI."""
    parser = argparse.ArgumentParser(
        prog="xbl3auth",
        description="Secure XBL3.0 token helper using MSAL and the OS keyring.",
    )
    parser.add_argument(
        "--client-id",
        help=(
            "Azure AD public client application ID. "
            "Defaults to XBL3AUTH_CLIENT_ID environment variable, "
            "then remote config, then the built-in client ID."
        ),
    )
    parser.add_argument(
        "--account-id",
        default="default",
        help="Logical account label used for keyring storage.",
    )
    parser.add_argument(
        "--print-json",
        action="store_true",
        help="Print token and metadata as JSON instead of plain text.",
    )
    parser.add_argument(
        "--show-state",
        action="store_true",
        help="Print non-secret state information and exit.",
    )
    parser.add_argument(
        "--clear-refresh-token",
        action="store_true",
        help="Clear any stored refresh token for this account and exit.",
    )
    return parser


def _fetch_remote_client_config(timeout: float = 2.0) -> Optional[Dict[str, Any]]:
    """Best-effort fetch of remote client configuration.

    The remote endpoint is expected to:
      - Accept a POST with JSON body: {ts, sig, version}
      - Validate the timestamp and signature
      - Return JSON containing at least: {enabled: bool, client_id: str, ...}

    Any network, parsing, or validation failure results in None.
    """
    if not REMOTE_CONFIG_URL:
        return None

    ts = int(time.time())
    sig = hashlib.sha256(str(ts).encode("ascii")).hexdigest()
    payload = {"ts": ts, "sig": sig, "version": "0.1.0"}

    headers = {
        "User-Agent": "xbl3auth/0.1.0",
        "Content-Type": "application/json",
    }

    try:
        resp = requests.post(
            REMOTE_CONFIG_URL,
            json=payload,
            timeout=timeout,
            headers=headers,
        )
        # Any non-200 from the worker means we ignore the remote config
        if resp.status_code != 200:
            return None

        data = resp.json()
        if not data.get("enabled", True):
            return None
        if "client_id" not in data:
            return None

        return data
    except Exception:
        # Network, timeout, or JSON decoding failure should not break the CLI.
        return None


def _resolve_client_id(cli_client_id: str | None) -> str:
    """Resolve the effective client ID.

    Precedence:
        1. --client-id argument
        2. XBL3AUTH_CLIENT_ID environment variable
        3. Remote config endpoint (best-effort)
        4. DEFAULT_CLIENT_ID constant
    """
    # 1) CLI flag
    if cli_client_id:
        return cli_client_id

    # 2) Env var
    env_id = os.getenv("XBL3AUTH_CLIENT_ID")
    if env_id:
        return env_id

    # 3) Remote config
    cfg = _fetch_remote_client_config()
    if cfg and cfg.get("client_id"):
        return cfg["client_id"]

    # 4) Hardcoded default
    return DEFAULT_CLIENT_ID


def main(argv: list[str] | None = None) -> int:
    """CLI entrypoint for xbl3auth.

    Args:
        argv: Optional list of command-line arguments. Defaults to sys.argv[1:].

    Returns:
        Zero on success, non-zero on failure.
    """
    if argv is None:
        argv = sys.argv[1:]

    parser = build_parser()
    args = parser.parse_args(argv)

    client_id = _resolve_client_id(args.client_id)
    config = XblAuthConfig(client_id=client_id)
    service = Xbl3AuthService(config, account_id=args.account_id)

    if args.clear_refresh_token:
        service.clear_refresh_token()
        print(f"Cleared stored refresh token for account: {args.account_id}")
        return 0

    if args.show_state:
        print(service.describe_state())
        return 0

    try:
        xbl3_token = service.get_xbl3_token()
    except Exception as exc:  # noqa: BLE001
        print(f"Error obtaining XBL3.0 token: {exc}", file=sys.stderr)
        return 1

    if args.print_json:
        print(json.dumps({"xbl3_token": xbl3_token}))
    else:
        print(xbl3_token)

    return 0
