"""
Nexus full-site API coverage (read-mostly + isolated CRUD with cleanup).
Runs after test_api.py patterns; safe on production when CRUD uses e2e-* names only.

Usage: python tests/test_full_site_api.py
Env: same as tests/test_api.py (loads .env NEXUS_TEST_*)
"""
from __future__ import annotations

import json
import os
import sys
import urllib.error
import urllib.request

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import test_api as base  # noqa: E402

base._load_credentials_from_env_file()

def _headers():
    h = {"Content-Type": "application/json"}
    if base.ACCESS_TOKEN:
        h["Authorization"] = f"Bearer {base.ACCESS_TOKEN}"
    return h


PASS = 0
FAIL = 0


def call(name: str, method: str, path: str, body=None, expect=200):
    global PASS, FAIL
    url = f"{base.BASE}{path}"
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(url, data=data, method=method, headers=_headers())
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            code = resp.getcode()
            raw = resp.read()
            if code == expect:
                PASS += 1
                print(f"  [PASS] {name}")
                if not raw:
                    return {}
                return json.loads(raw)
            FAIL += 1
            print(f"  [FAIL] {name}: expected {expect}, got {code}")
    except urllib.error.HTTPError as e:
        if e.code == expect:
            PASS += 1
            print(f"  [PASS] {name}")
            body_b = e.read()
            return json.loads(body_b) if body_b else {}
        FAIL += 1
        print(f"  [FAIL] {name}: HTTP {e.code} — {e.read().decode()[:180]}")
    except Exception as ex:
        FAIL += 1
        print(f"  [FAIL] {name}: {ex}")
    return None


def main() -> int:
    global PASS, FAIL
    print("=" * 50)
    print("Nexus Full-Site API Tests")
    print(f"  Target: {base.BASE}")
    print("=" * 50)

    base._load_credentials_from_env_file()
    base.test_plain_text("GET /health", "GET", "/health", expect_body="ok")

    login = base.test("POST /api/auth/login", "POST", "/api/auth/login", body={
        "username": base.ADMIN_USER,
        "password": base.ADMIN_PASSWORD,
    }, expect_code=200)
    if not login or not login.get("access_token"):
        print("FATAL: login failed")
        return 1
    base.ACCESS_TOKEN = login["access_token"]
    base.REFRESH_TOKEN = login.get("refresh_token", "")

    print("\n[Search & Alerts]")
    call("GET /api/search/", "GET", "/api/search/?q=test")
    call("GET /api/alert-history/", "GET", "/api/alert-history/?limit=5")
    call("GET /api/alert-history/stats", "GET", "/api/alert-history/stats")

    print("\n[Assets]")
    call("GET /api/assets/platforms", "GET", "/api/assets/platforms")
    call("GET /api/assets/nodes", "GET", "/api/assets/nodes")
    call("GET /api/assets/nodes/tree", "GET", "/api/assets/nodes/tree")
    call("GET /api/assets/ssh-sessions", "GET", "/api/assets/ssh-sessions?limit=5")
    call("GET /api/assets/command-logs", "GET", "/api/assets/command-logs?limit=5")

    plat = call("POST /api/assets/platforms (e2e)", "POST", "/api/assets/platforms", body={
        "name": "e2e-platform-full",
        "category": "test",
        "type": "linux",
    }, expect=201)
    plat_id = plat.get("id") if plat else None
    if plat_id:
        call(f"PUT /api/assets/platforms/{plat_id}", "PUT", f"/api/assets/platforms/{plat_id}", body={
            "description": "updated",
        })
        call(f"DELETE /api/assets/platforms/{plat_id}", "DELETE", f"/api/assets/platforms/{plat_id}", expect=204)

    print("\n[Servers meta]")
    call("GET /api/servers/categories", "GET", "/api/servers/categories")
    call("GET /api/servers/logs", "GET", "/api/servers/logs?per_page=5")
    call("GET /api/servers/meta/api_base_url", "GET", "/api/servers/meta/api_base_url")
    servers = call("GET /api/servers/", "GET", "/api/servers/?per_page=5")
    first_id = None
    if servers:
        items = servers.get("items") or servers.get("servers") or []
        if items and isinstance(items[0], dict):
            first_id = items[0].get("id")
    if first_id:
        call(f"GET /api/servers/{first_id}/logs", "GET", f"/api/servers/{first_id}/logs?limit=3")

    print("\n[Scripts & credentials]")
    call("GET /api/scripts/exec-config", "GET", "/api/scripts/exec-config")
    call("GET /api/scripts/executions", "GET", "/api/scripts/executions?limit=5")
    call("GET /api/scripts/credentials", "GET", "/api/scripts/credentials")
    call("GET /api/ssh-key-presets/", "GET", "/api/ssh-key-presets/")

    print("\n[Sync read-only / safe]")
    call("POST /api/sync/validate-source-path", "POST", "/api/sync/validate-source-path", body={
        "path": "/tmp",
    })
    if first_id:
        call("POST /api/sync/browse (real server)", "POST", "/api/sync/browse", body={
            "server_id": first_id,
            "path": "/tmp",
        })
        call("POST /api/auth/webssh-token", "POST", "/api/auth/webssh-token", body={
            "server_id": first_id,
        })

    print("\n[Settings & health detail]")
    call("GET /api/settings/", "GET", "/api/settings/")
    call("GET /api/settings/ip-allowlist", "GET", "/api/settings/ip-allowlist")
    call("GET /api/settings/bing-wallpapers", "GET", "/api/settings/bing-wallpapers")
    call("GET /health/detail", "GET", "/health/detail")

    print("\n[Terminal]")
    call("GET /api/terminal/quick-commands", "GET", "/api/terminal/quick-commands")

    print("\n[Auth refresh]")
    if base.REFRESH_TOKEN:
        call("POST /api/auth/refresh", "POST", "/api/auth/refresh", body={
            "refresh_token": base.REFRESH_TOKEN,
        })

    print(f"\n{'=' * 50}")
    print(f"Full-site API: {PASS}/{PASS + FAIL} passed, {FAIL} failed")
    return 0 if FAIL == 0 else 1


if __name__ == "__main__":
    sys.exit(main())
