"""Server-backed 14-day trial license issuance."""
from __future__ import annotations

import hashlib
import json
import os
import platform
import secrets
import ssl
import urllib.error
import urllib.request
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any

import config_manager as _config_manager
try:
    from config_manager import get_llm_config_dir as _get_llm_config_dir
except ImportError:
    _get_llm_config_dir = _config_manager.get_llm_config_dir if hasattr(_config_manager, 'get_llm_config_dir') else None
from license_server.config import (
    CLIENT_LICENSE_DIR,
    CLIENT_LICENSE_FILE,
    LICENSE_VERSION,
    PRODUCT_ID,
    TIERS,
)
from src.license.validator import _get_hardware_id, save_license
from src.openclaw_runtime import resolve_openclaw_config_path

TRIAL_DAYS = int(os.environ.get("GAT_TRIAL_DAYS", "14"))
TRIAL_STATE_FILE = CLIENT_LICENSE_DIR / ".trial_claimed"
DEFAULT_SERVER = os.environ.get("GAT_LICENSE_SERVER", "https://license.goldauto.top")


def can_claim_trial() -> bool:
    history = _load_trial_history()
    if not history:
        return True
    latest = history[-1]
    expires_at = str(latest.get("expires_at", "") or "")
    if not expires_at:
        return False
    try:
        is_expired = datetime.now(timezone.utc) > datetime.fromisoformat(expires_at)
    except ValueError:
        return False
    # ── 过期或硬件变更时允许重新申领 ──
    if is_expired:
        return True
    # 硬件变更: license 验证失败时允许重绑到新机器
    current_hwid = _get_hardware_id()
    bound_hwid = str(latest.get("hardware_id", "") or "")
    if bound_hwid and bound_hwid != current_hwid:
        return True  # 硬件变更，允许重新绑定
    return False


def _load_trial_history() -> list[dict[str, Any]]:
    if not TRIAL_STATE_FILE.exists():
        return []
    try:
        data = json.loads(TRIAL_STATE_FILE.read_text(encoding="utf-8"))
    except Exception:
        return []
    if isinstance(data, dict):
        claims = data.get("claims", [])
        if isinstance(claims, list):
            return [item for item in claims if isinstance(item, dict)]
        if data.get("claimed_at") or data.get("expires_at"):
            return [data]
    return []


def _record_trial_claim(result: dict[str, Any], identity_hash: str, hardware_id: str) -> None:
    history = _load_trial_history()
    license_payload = result.get("signed_license", {}).get("payload", {})
    action = str(result.get("action", "claimed") or "claimed")
    history.append(
        {
            "claimed_at": str(license_payload.get("issued_at", "") or datetime.now(timezone.utc).isoformat()),
            "expires_at": str(license_payload.get("expires_at", "") or ""),
            "hardware_id": hardware_id,
            "identity_hash": identity_hash,
            "action": action,
            "server_managed": bool(result.get("server_managed", not action.startswith("local_"))),
        }
    )
    CLIENT_LICENSE_DIR.mkdir(parents=True, exist_ok=True)
    TRIAL_STATE_FILE.write_text(
        json.dumps({"claims": history[-8:]}, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
    os.chmod(TRIAL_STATE_FILE, 0o600)


def _finalize_trial_claim(result: dict[str, Any], identity_hash: str, hardware_id: str) -> dict[str, Any]:
    signed = result.get("signed_license")
    if not result.get("ok") or not signed:
        return {
            "ok": False,
            "error": str(result.get("error", "TRIAL_CLAIM_FAILED") or "TRIAL_CLAIM_FAILED"),
            "message": str(result.get("message", "试用授权失败") or "试用授权失败"),
        }

    if not save_license(signed):
        return {"ok": False, "error": "TRIAL_SAVE_FAILED", "message": "试用授权已签发，但本地保存失败。"}

    from src.license.validator import load_license

    verified = load_license(auto_trial=False)
    if not verified or verified.get("error"):
        return {
            "ok": False,
            "error": "TRIAL_VERIFY_FAILED",
            "message": "试用授权已签发，但本地验证失败。",
        }

    _record_trial_claim(result, identity_hash, hardware_id)
    payload = signed.get("payload", {})
    expires_at = str(payload.get("expires_at", "") or "")
    days_left = 0
    if expires_at:
        try:
            exp = datetime.fromisoformat(expires_at)
            days_left = max(0, (exp - datetime.now(timezone.utc)).days)
        except ValueError:
            pass
    return {
        "ok": True,
        "tier": "trial",
        "expires": expires_at[:10],
        "days_left": days_left,
        "features": list(payload.get("features", ["basic"])),
        "action": result.get("action", "claimed"),
    }


def _hash_text(value: str) -> str:
    return hashlib.sha256(value.encode("utf-8")).hexdigest()


def _read_gateway_hint() -> str:
    gateway_token = Path.home() / ".openclaw" / "gateway.token"
    if gateway_token.exists():
        try:
            token = gateway_token.read_text(encoding="utf-8").strip()
            if token:
                return f"gateway:{token}"
        except OSError:
            pass
    return ""


def _read_provider_hints() -> list[str]:
    hints: list[str] = []
    config_path = resolve_openclaw_config_path() or (
        _get_llm_config_dir() / "openclaw.json"
        if _get_llm_config_dir
        else (Path.home() / ".openclaw" / "openclaw.json")
    )
    if not config_path.exists():
        return hints
    try:
        cfg = json.loads(config_path.read_text(encoding="utf-8"))
    except Exception:
        return hints
    providers = cfg.get("models", {}).get("providers", {})
    if not isinstance(providers, dict):
        return hints
    for provider_name, info in providers.items():
        if not isinstance(info, dict):
            continue
        api_key = str(info.get("apiKey", "") or "").strip()
        if api_key:
            hints.append(f"{provider_name}:{api_key}")
    return hints


def _collect_trial_identity_seeds() -> list[str]:
    seeds: list[str] = []
    gateway_hint = _read_gateway_hint()
    if gateway_hint:
        seeds.append(gateway_hint)
    seeds.extend(_read_provider_hints())
    username = (os.environ.get("USERNAME") or os.environ.get("USER") or "").strip().lower()
    if username:
        seeds.append(f"user:{username}")
    return list(dict.fromkeys(seed for seed in seeds if seed))


def _get_trial_identity_profile() -> tuple[str, list[str], str]:
    hardware_id = _get_hardware_id()
    stable_seeds = _collect_trial_identity_seeds()
    identity_aliases = [_hash_text(seed) for seed in stable_seeds]
    hardware_alias = _hash_text(f"hw:{hardware_id}")

    if stable_seeds:
        primary_seed = "stable:" + "|".join(sorted(stable_seeds))
        identity_hash = _hash_text(primary_seed)
    else:
        identity_hash = hardware_alias

    if hardware_alias not in identity_aliases:
        identity_aliases.append(hardware_alias)
    return identity_hash, identity_aliases, hardware_id


def _claim_trial_from_server(
    server_url: str,
    identity_hash: str,
    hardware_id: str,
    identity_aliases: list[str],
) -> dict[str, Any]:
    machine_name = platform.node() or "unknown"
    body = json.dumps(
        {
            "identity_hash": identity_hash,
            "identity_aliases": identity_aliases,
            "hardware_id": hardware_id,
            "machine_name": machine_name,
        }
    ).encode("utf-8")
    req = urllib.request.Request(
        f"{server_url.rstrip('/')}/trial/claim",
        data=body,
        headers={"Content-Type": "application/json"},
    )
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    with urllib.request.urlopen(req, timeout=15, context=ctx) as resp:
        return json.loads(resp.read().decode("utf-8"))


def claim_trial() -> dict[str, Any]:
    identity_hash, identity_aliases, hardware_id = _get_trial_identity_profile()
    try:
        result = _claim_trial_from_server(DEFAULT_SERVER, identity_hash, hardware_id, identity_aliases)
    except urllib.error.HTTPError as exc:
        try:
            result = json.loads(exc.read().decode("utf-8"))
        except Exception:
            result = {"ok": False, "error": f"HTTP_{exc.code}"}
    except Exception as exc:
        return {
            "ok": False,
            "error": "TRIAL_SERVER_UNREACHABLE",
            "message": f"无法连接试用授权服务器: {exc}",
        }

    return _finalize_trial_claim(result, identity_hash, hardware_id)


def get_trial_status() -> dict:
    """Get current trial status for display."""
    lic_file = CLIENT_LICENSE_FILE
    if not lic_file.exists():
        return {"status": "NO_TRIAL", "can_claim": can_claim_trial()}

    try:
        raw = json.loads(lic_file.read_text(encoding="utf-8"))
        payload = raw.get("signed_license", {}).get("payload", {})
        if not payload.get("is_trial"):
            return {"status": "PAID_LICENSE", "tier": payload.get("tier", "pro")}

        expires_at = payload.get("expires_at", "")
        if expires_at:
            exp = datetime.fromisoformat(expires_at)
            now = datetime.now(timezone.utc)
            days_left = max(0, (exp - now).days)
            if now > exp:
                return {"status": "TRIAL_EXPIRED", "days_left": 0, "expired_at": expires_at}
            return {"status": "TRIAL_ACTIVE", "days_left": days_left, "expires": expires_at[:10]}
    except Exception:
        pass

    return {"status": "NO_TRIAL", "can_claim": can_claim_trial()}
