from __future__ import annotations

import json
import os
import time
from pathlib import Path
from typing import Any
from urllib.error import URLError
from urllib.parse import urlencode
from urllib.request import urlopen

from flask import Flask, jsonify, send_from_directory
from flask_cors import CORS


app = Flask(__name__, static_folder="../frontend/dist", static_url_path="")
CORS(app, resources={r"/api/*": {"origins": "*"}})

MONERO_GET_INFO_URL = os.getenv("MONERO_GET_INFO_URL", "http://monerod.crypto.svc.cluster.local:18081/get_info")
VM_BASE_URL = os.getenv(
    "VM_BASE_URL",
    "http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428",
).rstrip("/")
VM_QUERY_TIMEOUT_SEC = float(os.getenv("VM_QUERY_TIMEOUT_SEC", "2"))
HTTP_CHECK_TIMEOUT_SEC = float(os.getenv("HTTP_CHECK_TIMEOUT_SEC", "2"))
LAB_STATUS_CACHE_SEC = float(os.getenv("LAB_STATUS_CACHE_SEC", "30"))
GRAFANA_HEALTH_URL = os.getenv("GRAFANA_HEALTH_URL", "https://metrics.bstein.dev/api/health")
OCEANUS_NODE_EXPORTER_URL = os.getenv("OCEANUS_NODE_EXPORTER_URL", "http://192.168.22.24:9100/metrics")

_LAB_STATUS_CACHE: dict[str, Any] = {"ts": 0.0, "value": None}

@app.route("/api/healthz")
def healthz() -> Any:
    return jsonify({"ok": True})


@app.route("/api/monero/get_info")
def monero_get_info() -> Any:
    try:
        with urlopen(MONERO_GET_INFO_URL, timeout=2) as resp:
            payload = json.loads(resp.read().decode("utf-8"))
        return jsonify(payload)
    except (URLError, TimeoutError, ValueError) as exc:
        return jsonify({"error": str(exc), "url": MONERO_GET_INFO_URL}), 503


def _vm_query(expr: str) -> float | None:
    url = f"{VM_BASE_URL}/api/v1/query?{urlencode({'query': expr})}"
    with urlopen(url, timeout=VM_QUERY_TIMEOUT_SEC) as resp:
        payload = json.loads(resp.read().decode("utf-8"))

    if payload.get("status") != "success":
        return None

    result = (payload.get("data") or {}).get("result") or []
    if not result:
        return None

    values: list[float] = []
    for item in result:
        try:
            values.append(float(item["value"][1]))
        except (KeyError, IndexError, TypeError, ValueError):
            continue

    if not values:
        return None

    return max(values)


def _http_ok(url: str, expect_substring: str | None = None) -> bool:
    try:
        with urlopen(url, timeout=HTTP_CHECK_TIMEOUT_SEC) as resp:
            if getattr(resp, "status", 200) != 200:
                return False
            if expect_substring:
                chunk = resp.read(4096).decode("utf-8", errors="ignore")
                return expect_substring in chunk
            return True
    except (URLError, TimeoutError, ValueError):
        return False


@app.route("/api/lab/status")
def lab_status() -> Any:
    now = time.time()
    cached = _LAB_STATUS_CACHE.get("value")
    if cached and (now - float(_LAB_STATUS_CACHE.get("ts", 0.0)) < LAB_STATUS_CACHE_SEC):
        return jsonify(cached)

    connected = False
    atlas_up = False
    atlas_known = False
    atlas_source = "unknown"
    oceanus_up = False
    oceanus_known = False
    oceanus_source = "unknown"

    try:
        atlas_value = _vm_query('max(up{job="kubernetes-apiservers"})')
        oceanus_value = _vm_query('max(up{instance=~"(titan-23|192.168.22.24)(:9100)?"})')
        connected = True
        atlas_known = atlas_value is not None
        atlas_up = bool(atlas_value and atlas_value > 0.5)
        atlas_source = "victoria-metrics"
        oceanus_known = oceanus_value is not None
        oceanus_up = bool(oceanus_value and oceanus_value > 0.5)
        oceanus_source = "victoria-metrics"
    except (URLError, TimeoutError, ValueError):
        atlas_value = None
        oceanus_value = None

    if not atlas_known:
        if _http_ok(GRAFANA_HEALTH_URL):
            connected = True
            atlas_known = True
            atlas_up = True
            atlas_source = "grafana-health"

    if not oceanus_up:
        if _http_ok(OCEANUS_NODE_EXPORTER_URL, expect_substring="node_exporter_build_info"):
            connected = True
            oceanus_known = True
            oceanus_up = True
            oceanus_source = "node-exporter"

    payload = {
        "connected": connected,
        "atlas": {"up": atlas_up, "known": atlas_known, "source": atlas_source},
        "oceanus": {"up": oceanus_up, "known": oceanus_known, "source": oceanus_source},
        "checked_at": int(now),
    }

    _LAB_STATUS_CACHE["ts"] = now
    _LAB_STATUS_CACHE["value"] = payload
    return jsonify(payload)


@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def serve_frontend(path: str) -> Any:
    dist_path = Path(app.static_folder)
    index_path = dist_path / "index.html"

    if dist_path.exists() and index_path.exists():
        target = dist_path / path
        if path and target.exists():
            return send_from_directory(app.static_folder, path)
        return send_from_directory(app.static_folder, "index.html")

    return jsonify(
        {
            "message": "Frontend not built yet. Run `npm install && npm run build` inside frontend/, then restart Flask.",
            "available_endpoints": ["/api/healthz", "/api/monero/get_info"],
        }
    )


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)
