"""GFW (Great Firewall) detection and China network utilities.

Detects if the app is running behind the GFW by probing
reachability of known-blocked services. Results are cached.

Used by license_manager and updater to select China-compatible
fallback strategies.
"""

from __future__ import annotations

import logging
import socket
import threading
import time

log = logging.getLogger(__name__)

_cached_result: bool | None = None
_cache_ts: float = 0.0
_CACHE_TTL = 300.0  # 5 minutes
_lock = threading.Lock()

# Chinese DNS servers — if reachable, likely in China
_CHINA_DNS = ["114.114.114.114", "223.5.5.5"]

# Services that are blocked by GFW
_BLOCKED_HOSTS = [
    ("api.github.com", 443),
    ("api.openai.com", 443),
]

# Services that work in China (for positive confirmation)
_CHINA_HOSTS = [
    ("api.deepseek.com", 443),
    ("dashscope.aliyuncs.com", 443),
]


def _can_connect(host: str, port: int, timeout: float = 2.0) -> bool:
    """Try a TCP connect to host:port."""
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        sock.connect((host, port))
        sock.close()
        return True
    except (OSError, socket.timeout):
        return False


def is_behind_gfw(force: bool = False) -> bool:
    """Detect if running behind China's Great Firewall.

    Heuristic:
    1. Can reach Chinese services (DeepSeek, Aliyun) → +1 China
    2. Cannot reach blocked services (GitHub, OpenAI) → +1 GFW
    3. Both → behind GFW

    Results cached for 5 minutes.

    Returns:
        True if likely behind GFW (in mainland China without VPN).
    """
    global _cached_result, _cache_ts

    now = time.monotonic()
    with _lock:
        if not force and _cached_result is not None and (now - _cache_ts) < _CACHE_TTL:
            return _cached_result

    china_reachable = 0
    blocked_reachable = 0

    # Check Chinese services (should be fast from China)
    for host, port in _CHINA_HOSTS:
        if _can_connect(host, port, timeout=3.0):
            china_reachable += 1

    # Check blocked services (should timeout from China)
    for host, port in _BLOCKED_HOSTS:
        if _can_connect(host, port, timeout=3.0):
            blocked_reachable += 1

    # Behind GFW: Chinese services work, blocked ones don't
    behind_gfw = china_reachable > 0 and blocked_reachable == 0

    with _lock:
        _cached_result = behind_gfw
        _cache_ts = now

    if behind_gfw:
        log.info("GFW detected: Chinese services OK, blocked services unreachable")
    else:
        log.debug("GFW not detected (or VPN active)")

    return behind_gfw


def is_behind_gfw_fast() -> bool:
    """Non-blocking cached check. Returns False if not yet probed."""
    with _lock:
        if _cached_result is not None:
            return _cached_result
    return False


def get_proxy_if_needed(manual_url: str = "") -> str:
    """Return a proxy URL if behind the GFW and a proxy is available.

    Convenience wrapper that combines GFW detection with proxy detection.
    Returns empty string if not behind GFW or no proxy found.
    """
    if not is_behind_gfw():
        return ""
    from .proxy_tunnel import get_proxy_url
    return get_proxy_url(manual_url=manual_url)


def probe_gfw_async(callback: callable | None = None) -> None:
    """Run GFW detection in background thread.

    Args:
        callback: Optional function called with (is_gfw: bool) when done.
    """
    def _run() -> None:
        result = is_behind_gfw(force=True)
        if callback:
            try:
                callback(result)
            except Exception:
                pass

    t = threading.Thread(target=_run, daemon=True, name="gfw-detect")
    t.start()
