import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Events } from "@wailsio/runtime";
import { Connection, WindowManager } from "@bindings/services";
import { ToggleSwitch } from "@/components/switches/ToggleSwitch.tsx";
import { useStatus } from "@/contexts/StatusContext.tsx";
import { useProfile } from "@/contexts/ProfileContext.tsx";
import { cn } from "@/lib/cn.ts";
import { errorDialog, formatErrorMessage } from "@/lib/errors.ts";
import {
    startConnection,
    EVENT_BROWSER_LOGIN_CANCEL,
    EVENT_TRIGGER_LOGIN,
} from "@/lib/connection.ts";
import { CopyToClipboard } from "@/components/CopyToClipboard";
import { TruncatedText } from "@/components/TruncatedText";
import { shortenDns } from "@/lib/formatters";
import { contentTop } from "@/components/empty-state/EmptyState";
import { Check as CheckIcon, ChevronDownIcon, Copy as CopyIcon } from "lucide-react";
import * as Popover from "@radix-ui/react-popover";
import netbirdFullLogo from "@/assets/logos/netbird-full.svg";

enum ConnectionState {
    Disconnected = "disconnected",
    Connecting = "connecting",
    Connected = "connected",
    Disconnecting = "disconnecting",
}

const STATUS_KEY: Record<ConnectionState, string> = {
    [ConnectionState.Disconnected]: "connect.status.disconnected",
    [ConnectionState.Connecting]: "connect.status.connecting",
    [ConnectionState.Connected]: "connect.status.connected",
    [ConnectionState.Disconnecting]: "connect.status.disconnecting",
};

const NEEDS_LOGIN_STATES = new Set(["NeedsLogin", "SessionExpired", "LoginFailed"]);

const FORCE_TOGGLE_DELAY_MS = 7000;

const errorMessage = formatErrorMessage;

export const MainConnectionStatusSwitch = () => {
    const { t } = useTranslation();
    const { status, refresh } = useStatus();
    const { activeProfile, username } = useProfile();

    const daemonState = status?.status ?? "Idle";
    const needsLogin = NEEDS_LOGIN_STATES.has(daemonState);
    const unreachable = daemonState === "DaemonUnavailable";

    type Action = "connect" | "logging-in" | "disconnect" | null;
    const [action, setAction] = useState<Action>(null);

    const loginGuard = useRef(false);
    const driveLogin = useCallback(() => {
        if (loginGuard.current) return;
        loginGuard.current = true;
        setAction("logging-in");
        void startConnection(() => {
            loginGuard.current = false;
            setAction(null);
            refresh().catch((err: unknown) => console.error("refresh after login failed", err));
        });
    }, [refresh]);

    const connState: ConnectionState = useMemo(() => {
        if (action === "disconnect" && daemonState === "Connected") {
            return ConnectionState.Disconnecting;
        }
        if ((action === "connect" || action === "logging-in") && daemonState !== "Connected") {
            return ConnectionState.Connecting;
        }
        switch (daemonState) {
            case "Connected":
                return ConnectionState.Connected;
            case "Connecting":
                return ConnectionState.Connecting;
            case "Idle":
            case "NeedsLogin":
            case "LoginFailed":
            case "SessionExpired":
            case "DaemonUnavailable":
                return ConnectionState.Disconnected;
            default:
                return ConnectionState.Disconnected;
        }
    }, [daemonState, action]);

    const connect = async () => {
        setAction("connect");
        try {
            await Connection.Up({
                profileName: activeProfile,
                username,
            });
            await refresh();
        } catch (e) {
            setAction(null);
            await refresh();
            await errorDialog({
                Title: t("connect.error.connectTitle"),
                Message: errorMessage(e),
            });
        }
    };

    const disconnect = async () => {
        setAction("disconnect");
        try {
            await Connection.Down();
            await refresh();
        } catch (e) {
            setAction(null);
            await refresh();
            await errorDialog({
                Title: t("connect.error.disconnectTitle"),
                Message: errorMessage(e),
            });
        }
    };

    const sawConnectingRef = useRef(false);

    useEffect(() => {
        if (action === null) {
            sawConnectingRef.current = false;
            return;
        }
        if (daemonState === "Connecting") {
            sawConnectingRef.current = true;
        }
        if (action === "connect") {
            if (needsLogin) {
                driveLogin();
                return;
            }
            if (daemonState === "Connected" || unreachable) {
                setAction(null);
                return;
            }
            if (sawConnectingRef.current && daemonState === "Idle") {
                setAction(null);
            }
            return;
        }
        if (action === "disconnect") {
            if (daemonState === "Idle" || daemonState === "Disconnected" || unreachable) {
                setAction(null);
            }
        }
    }, [action, daemonState, needsLogin, unreachable, driveLogin]);

    useEffect(() => {
        const off = Events.On(EVENT_TRIGGER_LOGIN, () => {
            driveLogin();
        });
        return () => off();
    }, [driveLogin]);

    const handleSwitch = (next: boolean) => {
        if (unreachable) return;
        if (isTransitioning) {
            if (canForceCancel) void forceCancel();
            return;
        }
        if (action !== null) return;
        if (needsLogin) {
            driveLogin();
            return;
        }
        if (next && connState === ConnectionState.Disconnected) {
            void connect();
        } else if (!next && connState === ConnectionState.Connected) {
            void disconnect();
        }
    };

    const isTransitioning =
        connState === ConnectionState.Connecting || connState === ConnectionState.Disconnecting;
    const isOn =
        connState === ConnectionState.Connected || connState === ConnectionState.Connecting;

    const [canForceCancel, setCanForceCancel] = useState(false);
    useEffect(() => {
        if (!isTransitioning) {
            setCanForceCancel(false);
            return;
        }
        const id = setTimeout(() => setCanForceCancel(true), FORCE_TOGGLE_DELAY_MS);
        return () => clearTimeout(id);
    }, [isTransitioning]);

    const forceCancel = async () => {
        if (action === "logging-in") {
            Events.Emit(EVENT_BROWSER_LOGIN_CANCEL).catch((err: unknown) =>
                console.error("emit browser-login cancel failed", err),
            );
        }
        WindowManager.CloseBrowserLogin().catch((err: unknown) =>
            console.warn("close browser-login window failed", err),
        );
        setAction("disconnect");
        try {
            await Connection.Down();
            await refresh();
        } catch (e) {
            setAction(null);
            await refresh();
            await errorDialog({
                Title: t("connect.error.disconnectTitle"),
                Message: errorMessage(e),
            });
        }
    };
    const show = connState === ConnectionState.Connected;
    const fqdn = status?.local.fqdn || "";
    const ip = status?.local.ip || "";
    const ipv6 = status?.local.ipv6 || "";

    return (
        <div
            className={cn("flex flex-col h-full w-full items-center gap-4", "relative")}
            style={{ top: contentTop("11.7rem") }}
        >
            <img
                src={netbirdFullLogo}
                alt={"NetBird"}
                className={"h-7 w-auto select-none mb-4 wails-no-draggable"}
                draggable={false}
            />

            <ToggleSwitch
                size={"large"}
                checked={isOn}
                onCheckedChange={handleSwitch}
                disabled={(isTransitioning && !canForceCancel) || unreachable}
                className={cn(unreachable && "opacity-80", isTransitioning && "animate-pulse")}
            />

            <div className={"flex flex-col items-center"}>
                <h1
                    className={
                        "text-sm font-medium text-nb-gray-200 tracking-wide transition-colors duration-300 select-none wails-no-draggable mb-1"
                    }
                >
                    {t(STATUS_KEY[connState])}
                </h1>
                <CopyToClipboard
                    message={fqdn}
                    variant={"bright"}
                    iconClassName={"-top-px"}
                    className={cn(
                        "min-h-[1em] transition-opacity duration-300 max-w-full",
                        "relative left-[0.55rem]",
                        show && fqdn ? "opacity-100" : "opacity-0 pointer-events-none",
                    )}
                >
                    <TruncatedText
                        text={shortenDns(fqdn) || " "}
                        className={
                            "block font-mono text-[0.8rem] leading-tight text-nb-gray-300 truncate max-w-[310px]"
                        }
                    />
                </CopyToClipboard>
                <LocalIpLine ip={ip} ipv6={ipv6} show={show} />
            </div>
        </div>
    );
};

const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boolean }) => {
    const [open, setOpen] = useState(false);
    const hasV6 = !!ipv6;

    if (!hasV6) {
        return (
            <CopyToClipboard
                message={ip}
                variant={"bright"}
                className={cn(
                    "min-h-[1em] transition-opacity duration-300",
                    "relative left-[0.55rem]",
                    show && ip ? "opacity-100" : "opacity-0 pointer-events-none",
                )}
            >
                <span className={"font-mono text-[0.8rem] leading-tight text-nb-gray-300"}>
                    {ip || " "}
                </span>
            </CopyToClipboard>
        );
    }

    return (
        <div
            className={cn(
                "min-h-[1em] transition-opacity duration-300 max-w-full",
                "relative wails-no-draggable",
                show && ip ? "opacity-100" : "opacity-0 pointer-events-none",
            )}
        >
            <Popover.Root open={open} onOpenChange={setOpen}>
                <Popover.Trigger asChild>
                    <button
                        type={"button"}
                        className={cn(
                            "group relative inline-flex items-center outline-none cursor-default",
                            "transition-colors",
                        )}
                    >
                        <span
                            className={cn(
                                "font-mono text-[0.8rem] leading-tight text-nb-gray-300 transition-colors",
                                "group-hover:text-nb-gray-200",
                                "group-data-[state=open]:text-nb-gray-200",
                            )}
                        >
                            {ip || " "}
                        </span>
                        <ChevronDownIcon
                            size={14}
                            className={cn(
                                "absolute -right-5 top-1/2 -translate-y-1/2",
                                "shrink-0 text-nb-gray-300 transition-colors",
                                "group-hover:text-nb-gray-200",
                                "group-data-[state=open]:text-nb-gray-200",
                            )}
                        />
                    </button>
                </Popover.Trigger>
                <Popover.Portal>
                    <Popover.Content
                        side={"bottom"}
                        align={"center"}
                        sideOffset={6}
                        onOpenAutoFocus={(e) => e.preventDefault()}
                        className={cn(
                            "z-50 min-w-64 max-w-[280px] overflow-hidden",
                            "rounded-lg border border-nb-gray-900 bg-nb-gray-935",
                            "p-1 shadow-lg outline-none text-nb-gray-200",
                            "flex flex-col",
                        )}
                    >
                        <IpRow value={ip} />
                        <div className={"-mx-1 my-1 h-px bg-nb-gray-910"} />
                        <IpRow value={ipv6} />
                    </Popover.Content>
                </Popover.Portal>
            </Popover.Root>
        </div>
    );
};

const IpRow = ({ value }: { value: string }) => {
    const [copied, setCopied] = useState(false);
    const handleClick = async () => {
        if (!value) return;
        try {
            await navigator.clipboard.writeText(value);
            setCopied(true);
            setTimeout(() => setCopied(false), 500);
        } catch (e) {
            console.warn("copy IP to clipboard failed", e);
        }
    };
    return (
        <button
            type={"button"}
            onClick={handleClick}
            className={cn(
                "group/iprow relative flex items-center justify-between gap-3",
                "rounded-md px-2 py-1.5 text-left",
                "text-nb-gray-200 hover:bg-nb-gray-900 hover:text-nb-gray-50",
                "transition-colors outline-none cursor-default",
            )}
        >
            <span className={"font-mono text-[0.75rem] truncate min-w-0"}>{value}</span>
            <span className={"shrink-0 inline-flex items-center text-nb-gray-200"}>
                {copied ? <CheckIcon size={11} /> : <CopyIcon size={11} />}
            </span>
        </button>
    );
};
