"use client";

import { useMemo, useRef, useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useParams, useRouter, useSearchParams } from "next/navigation";
import Link from "next/link";
import { api, GenerateKeyOptions, ServerStatus, vulnerabilities, workloads as workloadsApi } from "@/lib/api";
import { Badge } from "@/components/ui";
import { useLicense } from "@/lib/useLicense";
import { TagChips } from "@/components/servers/TagChips";
import { ServerVulnerabilities } from "@/components/vulnerabilities/ServerVulnerabilities";
import { WorkloadList } from "@/components/workloads/WorkloadList";
import { useAuth } from "@/components/AuthProvider";
import { GenerateKeyModal } from "@/components/servers/GenerateKeyModal";
import { VitalsRail } from "@/components/servers/VitalsRail";
import { ServerTabs, type TabId, type TabSpec } from "@/components/servers/ServerTabs";
import { ServerActionsMenu, type ServerAction } from "@/components/servers/ServerActionsMenu";
import { ArrowUpCircleIcon, ConsoleIcon, KeyIcon, RefreshIcon, ShieldIcon, TrashIcon } from "@/components/servers/icons";
import { OverviewTab, type Attention } from "@/components/servers/tabs/OverviewTab";
import { AccessTab } from "@/components/servers/tabs/AccessTab";
import { MaintenanceTab } from "@/components/servers/tabs/MaintenanceTab";

/*
 * One server, as a faceplate over five tabs.
 *
 * The page used to stack every panel it had — agent updater, inventory,
 * details, vulnerabilities, workloads, keys — so the answer to "is this machine
 * healthy" was several screens below the answer to "which agent build is on
 * it". Identity, status and the four live readings now stay pinned; everything
 * else is a tab, and the tab labels carry counts so a problem on a tab nobody
 * is looking at still announces itself.
 */

const TAB_IDS: TabId[] = ["overview", "workloads", "security", "access", "maintenance"];

function statusVariant(status: ServerStatus) {
    switch (status) {
        case "active":
            return "success";
        case "pending":
            return "warning";
        case "offline":
            return "danger";
    }
}

export default function ServerDetailPage() {
    const params = useParams();
    const router = useRouter();
    const searchParams = useSearchParams();
    const queryClient = useQueryClient();
    const serverId = params.id as string;

    const [showGenerateModal, setShowGenerateModal] = useState(false);
    const [updateSuccess, setUpdateSuccess] = useState(false);
    const [applySuccess, setApplySuccess] = useState(false);
    const panelsRef = useRef<HTMLDivElement>(null);

    const { hasFeature } = useLicense();
    // Control actions and log reads are owner|admin server-side; the UI matches
    // so a member is not offered buttons the API will refuse.
    const { isAdmin } = useAuth();
    const consoleAllowed = hasFeature("console");

    const tabParam = searchParams.get("tab") as TabId | null;
    const activeTab: TabId = tabParam && TAB_IDS.includes(tabParam) ? tabParam : "overview";

    /** The tab lives in the URL so an alert, a bookmark or a browser Back can
     *  name one. replace, not push — five tabs of history between two pages is
     *  a Back button that does not go back. */
    function selectTab(tab: TabId) {
        const next = new URLSearchParams(searchParams.toString());
        if (tab === "overview") next.delete("tab");
        else next.set("tab", tab);
        const query = next.toString();
        router.replace(query ? `?${query}` : `/servers/${serverId}`, { scroll: false });
        panelsRef.current?.scrollIntoView({ block: "start", behavior: "smooth" });
    }

    const {
        data: server,
        isLoading,
        error,
    } = useQuery({
        queryKey: ["servers", serverId],
        queryFn: () => api.getServer(serverId),
        refetchInterval: 30_000,
    });

    const { data: latestVersion } = useQuery({
        queryKey: ["agent-latest-version"],
        queryFn: () => api.getLatestAgentVersion(),
        staleTime: 5 * 60_000,
    });

    // Both share their tab component's query key, so the count on the label and
    // the list inside the tab are one fetch, not two.
    const { data: findings } = useQuery({
        queryKey: ["vulnerabilities", "server", serverId],
        queryFn: () => vulnerabilities.forServer(serverId),
    });

    const { data: workloadSnapshot } = useQuery({
        queryKey: ["workloads", serverId],
        queryFn: () => workloadsApi.forServer(serverId),
    });

    const { mutate: generateKey, isPending: isGenerating } = useMutation({
        mutationFn: (opts: GenerateKeyOptions) => api.generateKeyForServer(serverId, opts),
        onSuccess: () => {
            setShowGenerateModal(false);
            queryClient.invalidateQueries({ queryKey: ["servers", serverId] });
            queryClient.invalidateQueries({ queryKey: ["keys"] });
        },
    });

    const { mutate: triggerUpdate, isPending: isUpdating } = useMutation({
        mutationFn: () => api.updateAgent(serverId),
        onSuccess: () => {
            setUpdateSuccess(true);
            setTimeout(() => setUpdateSuccess(false), 4000);
        },
    });

    const { mutate: applyUpdates, isPending: isApplying } = useMutation({
        mutationFn: () => api.applyUpdates(serverId),
        onSuccess: () => {
            setApplySuccess(true);
            setTimeout(() => setApplySuccess(false), 4000);
        },
    });

    const { mutate: refreshWorkloads } = useMutation({
        mutationFn: () => workloadsApi.refresh(serverId),
        onSuccess: () => setTimeout(() => queryClient.invalidateQueries({ queryKey: ["workloads", serverId] }), 1500),
    });

    const { mutate: deleteServer, isPending: isDeleting } = useMutation({
        mutationFn: () => api.deleteServer(serverId),
        onSuccess: () => {
            queryClient.invalidateQueries({ queryKey: ["servers"] });
            router.push("/servers");
        },
    });

    const openFindings = useMemo(() => (findings ?? []).filter((f) => f.state === "open"), [findings]);
    const seriousFindings = openFindings.filter((f) => f.severity === "critical" || f.severity === "high").length;
    const updateCount = server?.available_updates?.length ?? 0;
    const workloadCount = workloadSnapshot?.workloads?.length ?? 0;
    const activeKeys = (server?.keys ?? []).filter((a) => a.key && !a.revoked_at).length;
    const agentOutOfDate = !!latestVersion && !!server?.agent_version && server.agent_version !== latestVersion.version;

    const attention: Attention[] = useMemo(() => {
        if (!server) return [];
        const items: Attention[] = [];

        for (const p of server.inventory?.partitions ?? []) {
            const pct = p.total_bytes > 0 ? (p.used_bytes / p.total_bytes) * 100 : 0;
            if (pct >= 90) {
                items.push({
                    tone: "danger",
                    title: `${p.mountpoint} is ${pct.toFixed(0)}% full`,
                    detail: `${((p.total_bytes - p.used_bytes) / 1024 ** 3).toFixed(1)} GB free`,
                    goTo: "overview",
                    action: "View storage",
                });
            }
        }
        if (seriousFindings > 0) {
            items.push({
                tone: "danger",
                title: `${seriousFindings} critical or high severity finding${seriousFindings !== 1 ? "s" : ""}`,
                detail: openFindings
                    .slice(0, 3)
                    .map((f) => f.package_name)
                    .join(", "),
                goTo: "security",
                action: "Review",
            });
        }
        if (updateCount > 0) {
            items.push({
                tone: "warning",
                title: `${updateCount} OS update${updateCount !== 1 ? "s" : ""} pending`,
                detail: server.updates_checked_at ? `checked ${new Date(server.updates_checked_at).toLocaleString()}` : "never checked",
                goTo: "maintenance",
                action: "Apply",
            });
        }
        if (agentOutOfDate) {
            items.push({
                tone: "warning",
                title: `Agent is behind v${latestVersion!.version}`,
                detail: `running v${server.agent_version}`,
                goTo: "maintenance",
                action: "Update",
            });
        }
        return items;
    }, [server, seriousFindings, openFindings, updateCount, agentOutOfDate, latestVersion]);

    if (isLoading) {
        return (
            <div className="flex h-full items-center justify-center">
                <div className="h-8 w-8 animate-spin rounded-full border-2 border-border border-t-accent" />
            </div>
        );
    }

    if (error || !server) {
        return (
            <div className="p-4 sm:p-6 lg:p-8">
                <div className="rounded border border-danger/30 bg-danger/10 p-4 text-danger">Server not found or failed to load.</div>
            </div>
        );
    }

    const protocols = server.console_protocols ?? [];

    const actions: ServerAction[] = [
        ...(protocols.length > 0
            ? protocols.map((p, i) => ({
                  group: i === 0 ? "Console" : undefined,
                  label: `Connect ${p.toUpperCase()}`,
                  icon: <ConsoleIcon />,
                  href: `/servers/${serverId}/console?protocol=${p}`,
                  // Offered disabled rather than hidden when the licence does not
                  // include the console: a customer cannot buy what they cannot see.
                  disabled: !consoleAllowed,
                  title: consoleAllowed ? undefined : "Upgrade to use the browser console",
              }))
            : [{ group: "Console", label: "No console protocol", icon: <ConsoleIcon />, disabled: true, title: "This host reports no console protocol" }]),
        { group: "Manage", label: "Generate SSH key", icon: <KeyIcon />, onSelect: () => setShowGenerateModal(true), separated: true },
        { label: "Refresh workloads", icon: <RefreshIcon />, onSelect: () => refreshWorkloads() },
        {
            label: "Update agent",
            icon: <ArrowUpCircleIcon />,
            onSelect: () => triggerUpdate(),
            disabled: server.status !== "active" || isUpdating,
            title: server.status !== "active" ? "Agent must be online to update" : undefined,
        },
        ...(updateCount > 0 ? [{ label: `Apply ${updateCount} OS update${updateCount !== 1 ? "s" : ""}`, icon: <ShieldIcon />, onSelect: () => selectTab("maintenance") }] : []),
        { label: "Remove server", icon: <TrashIcon />, onSelect: () => selectTab("maintenance"), danger: true, separated: true },
    ];

    const tabs: TabSpec[] = [
        { id: "overview", label: "Overview", count: attention.length, tone: attention.some((a) => a.tone === "danger") ? "danger" : "warning" },
        { id: "workloads", label: "Workloads", count: workloadCount },
        { id: "security", label: "Security", count: openFindings.length, tone: seriousFindings > 0 ? "danger" : "neutral" },
        { id: "access", label: "Access", count: activeKeys },
        { id: "maintenance", label: "Maintenance", count: updateCount, tone: "warning" },
    ];

    return (
        <>
            {showGenerateModal && <GenerateKeyModal onClose={() => setShowGenerateModal(false)} onSubmit={(opts) => generateKey(opts)} isPending={isGenerating} />}

            {/*
             * The faceplate sticks under whichever chrome is above it: the mobile
             * top bar below lg, nothing above it. z-20 keeps it under that bar
             * (z-40) and under the nav drawer (z-50). The scroll container is
             * AppShell's column, not the window, which is what sticky anchors to.
             */}
            <div className="sticky top-14 z-20 border-b border-border bg-background/90 px-4 pt-4 backdrop-blur sm:px-6 lg:top-0 lg:px-8">
                <Link href="/servers" className="text-sm text-text-secondary transition-colors hover:text-text-primary">
                    ← Servers
                </Link>

                <div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-2">
                    <h1 className="text-2xl font-bold text-text-primary">{server.hostname}</h1>
                    <Badge variant={statusVariant(server.status)}>{server.status}</Badge>
                    <span className="font-mono text-sm text-text-secondary">{server.ip_address}</span>
                    <div className="ml-auto">
                        <ServerActionsMenu actions={actions} />
                    </div>
                </div>

                <div className="mt-2">
                    <TagChips serverId={server.server_id} tags={server.tags} editable />
                </div>

                <VitalsRail server={server} agentUpToDate={latestVersion && server.agent_version ? !agentOutOfDate : undefined} />

                <div className="mt-3">
                    <ServerTabs tabs={tabs} active={activeTab} onSelect={selectTab} />
                </div>
            </div>

            <div ref={panelsRef} className="p-4 sm:p-6 lg:p-8">
                <div role="tabpanel" id={`server-panel-${activeTab}`} aria-labelledby={`server-tab-${activeTab}`}>
                    {activeTab === "overview" && <OverviewTab server={server} attention={attention} onGoTo={selectTab} />}
                    {activeTab === "workloads" && <WorkloadList serverId={server.server_id} canControl={isAdmin} />}
                    {activeTab === "security" && <ServerVulnerabilities serverId={server.server_id} />}
                    {activeTab === "access" && <AccessTab server={server} onGenerateKey={() => setShowGenerateModal(true)} />}
                    {activeTab === "maintenance" && (
                        <MaintenanceTab
                            server={server}
                            latestVersion={latestVersion?.version}
                            onApplyUpdates={() => applyUpdates()}
                            isApplying={isApplying}
                            applySuccess={applySuccess}
                            onUpdateAgent={() => triggerUpdate()}
                            isUpdatingAgent={isUpdating}
                            updateAgentSuccess={updateSuccess}
                            onDelete={() => deleteServer()}
                            isDeleting={isDeleting}
                        />
                    )}
                </div>
            </div>
        </>
    );
}
