# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# Licensed under the MIT License

"""Server provisioning routes — /api/server/*."""

from __future__ import annotations

import json
import logging
import os

from flask import Blueprint, Response, jsonify, request, send_file

from ..auth import require_auth
from ..helpers import get_agent, get_service_manager, get_wizard_host, DATA_DIR

logger = logging.getLogger(__name__)

bp = Blueprint("server", __name__)




# ============================================================
# Server
# ============================================================


@bp.route("/api/server/runtime")
@require_auth
def api_server_runtime():
    """Get detected container runtime info.

    Query params:
        refresh: If "true", invalidates the cached runtime and re-detects.
                 Use after installing a new runtime (e.g. Podman).
    """
    mgr = get_service_manager()
    if request.args.get("refresh", "").lower() == "true":
        mgr.invalidate_runtime()
    return jsonify(mgr.get_runtime_info())


@bp.route("/api/server/security")
@require_auth
def api_server_security():
    """Get server security posture summary."""
    mgr = get_service_manager()
    return jsonify(mgr.get_security_posture())


@bp.route("/api/apps")
@require_auth
def api_apps():
    """Get running services with web UI URLs for the Apps panel."""
    mgr = get_service_manager()
    apps = []
    for s in mgr.get_all_statuses():
        if s.get("status") != "running":
            continue
        if not s.get("web_ui", True):
            continue
        url = s.get("url", "")
        if not url:
            port = s.get("health_check_port", 0)
            if port:
                url = f"http://localhost:{port}"
        if not url:
            continue
        apps.append(
            {
                "key": s.get("key", ""),
                "display_name": s.get("display_name", s.get("key", "")),
                "icon": s.get("icon", "fa-cube"),
                "url": url,
                "description": s.get("description", ""),
                "doc_url": s.get("doc_url", ""),
            }
        )
    return jsonify({"apps": apps})


@bp.route("/api/server/status")
@require_auth
def api_server_status():
    """Get status of all managed services, with health checks for running ones."""
    mgr = get_service_manager()
    statuses = mgr.get_all_statuses()
    for svc in statuses:
        if svc.get("status") == "running":
            try:
                hc = mgr.health_check(svc["key"])
                svc["health"] = "healthy" if hc.get("healthy") else "unhealthy"
                svc["health_url"] = hc.get("url", "")
                svc["health_error"] = hc.get("error", "")
            except Exception:
                svc["health"] = "unknown"
    return jsonify(statuses)


@bp.route("/api/server/provision/<service>", methods=["POST"])
@require_auth
def api_server_provision(service):
    """Provision (pull + create) a service."""
    try:
        mgr = get_service_manager()
        result = mgr.provision(service)
        # Include setup notes if available
        try:
            from familiar.services.specs import SERVICE_SPECS

            spec = SERVICE_SPECS.get(service)
            if spec and spec.setup_notes:
                result["setup_notes"] = spec.setup_notes
        except Exception:
            pass
        return jsonify(result)
    except Exception as exc:
        return jsonify({"success": False, "error": str(exc)}), 500


@bp.route("/api/server/start/<service>", methods=["POST"])
@require_auth
def api_server_start(service):
    """Start a provisioned service."""
    try:
        mgr = get_service_manager()
        result = mgr.start(service)
        return jsonify(result)
    except Exception as exc:
        return jsonify({"success": False, "error": str(exc)}), 500


@bp.route("/api/server/stop/<service>", methods=["POST"])
@require_auth
def api_server_stop(service):
    """Stop a running service."""
    mgr = get_service_manager()
    result = mgr.stop(service)
    return jsonify(result)


@bp.route("/api/server/restart/<service>", methods=["POST"])
@require_auth
def api_server_restart(service):
    """Restart a service."""
    mgr = get_service_manager()
    result = mgr.restart(service)
    return jsonify(result)


@bp.route("/api/server/toggle/<service>", methods=["POST"])
@require_auth
def api_server_toggle(service):
    """Toggle a service: stop if running, start if stopped."""
    mgr = get_service_manager()
    status = mgr.get_status(service)
    if status.get("status") == "running":
        result = mgr.stop(service)
        result["action"] = "stopped"
    else:
        result = mgr.start(service)
        result["action"] = "started"
    return jsonify(result)


@bp.route("/api/server/remove/<service>", methods=["POST"])
@require_auth
def api_server_remove(service):
    """Remove a service container, optionally with data."""
    mgr = get_service_manager()
    remove_data = False
    if request.is_json:
        remove_data = request.json.get("remove_data", False)
    result = mgr.remove(service, remove_data=remove_data)
    return jsonify(result)


@bp.route("/api/server/logs/<service>")
@require_auth
def api_server_logs(service):
    """Get recent logs for a service."""
    mgr = get_service_manager()
    tail = request.args.get("tail", 100, type=int)
    result = mgr.get_logs(service, tail=tail)
    return jsonify(result)


@bp.route("/api/server/credentials")
@require_auth
def api_server_credentials():
    """Return all service credentials aggregated from three sources.

    1. Auto-generated secrets (secrets.json)
    2. Default upstream credentials (baked into container images)
    3. Service URLs for context

    Localhost-only by design — never expose credentials over the network.
    """
    import json as _json

    from familiar.services.specs import SERVICE_SPECS

    mgr = get_service_manager()
    statuses = mgr.get_all_statuses()
    running_keys = {s["key"] for s in statuses if s.get("status") == "running"}

    # Load auto-generated secrets (decrypted via credentials module)
    try:
        from familiar.services.credentials import _load_all as _load_all_creds

        generated: dict = _load_all_creds()
    except Exception:
        generated = {}

    services = []
    for key in sorted(running_keys):
        spec = SERVICE_SPECS.get(key)
        if not spec:
            continue

        entry: dict = {
            "key": key,
            "display_name": spec.display_name,
            "icon": spec.icon,
            "url": "",
        }

        # Find URL from status
        for s in statuses:
            if s.get("key") == key:
                entry["url"] = s.get("url", "")
                break

        # Default upstream credentials
        if spec.default_credentials:
            entry["default_credentials"] = spec.default_credentials

        # Auto-generated credentials
        if key in generated and generated[key]:
            entry["generated_credentials"] = {k: v for k, v in generated[key].items()}

        # Setup notes
        if spec.setup_notes:
            entry["setup_notes"] = spec.setup_notes

        # Only include services that have some credential info
        if any(k in entry for k in ("default_credentials", "generated_credentials", "setup_notes")):
            services.append(entry)

    return jsonify({"services": services})

