"use client";

import "@xterm/xterm/css/xterm.css";
import { useEffect, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { GetBrowserTargetResponse } from "@server/routers/browserGatewayTarget";
import {
    Card,
    CardContent,
    CardHeader,
    CardTitle,
    CardDescription
} from "@app/components/ui/card";
import Link from "next/link";
import { ExternalLink, Loader2, AlertCircle } from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { cn } from "@app/lib/cn";
import type { SignSshKeyResponse } from "@server/routers/ssh/types";
import { useTranslations } from "next-intl";

type AuthTab = "password" | "privateKey";

type FormState = {
    username: string;
    password: string;
    privateKey: string;
};

type ConnectCredentials = {
    username: string;
    password?: string;
    privateKey?: string;
    certificate?: string;
};

export default function SshClient({
    target,
    error,
    signedKeyData,
    privateKey: signedPrivateKey
}: {
    target: GetBrowserTargetResponse | null;
    error: string | null;
    signedKeyData?: SignSshKeyResponse | null;
    privateKey?: string | null;
}) {
    const STORAGE_KEY = "pangolin_ssh_credentials";

    const [form, setForm] = useState<FormState>(() => {
        try {
            const saved = localStorage.getItem(STORAGE_KEY);
            if (saved) return JSON.parse(saved) as FormState;
        } catch {
            // ignore
        }
        return { username: "", password: "", privateKey: "" };
    });

    const t = useTranslations();

    const [authTab, setAuthTab] = useState<AuthTab>("password");

    function handleKeyFile(e: React.ChangeEvent<HTMLInputElement>) {
        const file = e.target.files?.[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = (ev) => {
            const text = ev.target?.result;
            if (typeof text === "string") {
                setForm((prev) => ({ ...prev, privateKey: text }));
            }
        };
        reader.readAsText(file);
        // Reset input so the same file can be re-selected if needed.
        e.target.value = "";
    }

    const [connected, setConnected] = useState(false);
    const [connecting, setConnecting] = useState(false);
    const [connectError, setConnectError] = useState<string | null>(null);

    const terminalRef = useRef<HTMLDivElement>(null);
    const xtermRef = useRef<import("@xterm/xterm").Terminal | null>(null);
    const fitAddonRef = useRef<import("@xterm/addon-fit").FitAddon | null>(
        null
    );
    const wsRef = useRef<WebSocket | null>(null);

    // Mount the terminal div once connected.
    useEffect(() => {
        if (!connected || !terminalRef.current) return;

        let cancelled = false;

        (async () => {
            const [{ Terminal }, { FitAddon }, { WebLinksAddon }] =
                await Promise.all([
                    import("@xterm/xterm"),
                    import("@xterm/addon-fit"),
                    import("@xterm/addon-web-links")
                ]);
            if (cancelled || !terminalRef.current) return;

            const terminal = new Terminal({
                cursorBlink: true,
                fontSize: 14,
                fontFamily: "Menlo, Monaco, 'Courier New', monospace",
                theme: {
                    background: "#0d0d0d",
                    foreground: "#f0f0f0"
                },
                scrollback: 5000
            });

            const fitAddon = new FitAddon();
            const webLinksAddon = new WebLinksAddon();
            terminal.loadAddon(fitAddon);
            terminal.loadAddon(webLinksAddon);

            terminal.open(terminalRef.current);
            fitAddon.fit();

            xtermRef.current = terminal;
            fitAddonRef.current = fitAddon;

            // Send user keystrokes to the WebSocket.
            terminal.onData((data) => {
                if (wsRef.current?.readyState === WebSocket.OPEN) {
                    wsRef.current.send(JSON.stringify({ type: "data", data }));
                }
            });

            // Send resize events.
            terminal.onResize(({ cols, rows }) => {
                if (wsRef.current?.readyState === WebSocket.OPEN) {
                    wsRef.current.send(
                        JSON.stringify({ type: "resize", cols, rows })
                    );
                }
            });

            // Send the initial size once the terminal is rendered.
            const { cols, rows } = terminal;
            if (wsRef.current?.readyState === WebSocket.OPEN) {
                wsRef.current.send(
                    JSON.stringify({ type: "resize", cols, rows })
                );
            }
        })().catch(console.error);

        return () => {
            cancelled = true;
        };
    }, [connected]);

    // Refit terminal when the window resizes.
    useEffect(() => {
        const onResize = () => fitAddonRef.current?.fit();
        window.addEventListener("resize", onResize);
        return () => window.removeEventListener("resize", onResize);
    }, []);

    // Cleanup on unmount.
    useEffect(() => {
        return () => {
            wsRef.current?.close();
            xtermRef.current?.dispose();
        };
    }, []);

    // Auto-connect when signed key data is provided (push PAM mode).
    useEffect(() => {
        if (signedKeyData && signedPrivateKey && target) {
            connect({
                username: signedKeyData.sshUsername,
                privateKey: signedPrivateKey,
                certificate: signedKeyData.certificate
            });
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    function connect(override?: ConnectCredentials) {
        setConnectError(null);
        setConnecting(true);

        if (!target) {
            setConnectError(t("sshErrorNoTarget"));
            setConnecting(false);
            return;
        }

        const username = override?.username ?? form.username;
        const password =
            override?.password ?? (authTab === "password" ? form.password : "");
        const privateKey =
            override?.privateKey ??
            (authTab === "privateKey" ? form.privateKey : "");
        const certificate = override?.certificate;

        const proxyAddress = `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}/gateway/ssh`;
        const url = new URL(proxyAddress);
        url.searchParams.set(
            "mode",
            target.authDaemonMode === "native" ? "native" : "proxy"
        );
        if (target.authDaemonMode !== "native") {
            url.searchParams.set("host", target.ip ?? "");
            url.searchParams.set("port", String(target.port ?? 22));
        }
        url.searchParams.set("username", username);
        url.searchParams.set("authToken", target.authToken ?? "");

        const ws = new WebSocket(url.toString(), ["ssh"]);
        wsRef.current = ws;

        // Track whether the server has confirmed auth by sending the first
        // data frame. Until then, errors are shown in the login form.
        let authConfirmed = false;
        let authErrorShown = false;

        ws.onopen = () => {
            // Send credentials as the first frame so the proxy can complete
            // SSH authentication before piping pty data. Stay in "connecting"
            // state until the server responds — this prevents the flash to the
            // terminal page that would occur if we set connected=true here.
            ws.send(
                JSON.stringify({
                    type: "auth",
                    password,
                    privateKey,
                    certificate
                })
            );
            if (!override) {
                try {
                    localStorage.setItem(STORAGE_KEY, JSON.stringify(form));
                } catch {
                    // ignore
                }
            }
        };

        ws.onmessage = (evt) => {
            if (typeof evt.data === "string") {
                try {
                    const msg = JSON.parse(evt.data as string) as {
                        type: string;
                        data?: string;
                        error?: string;
                    };
                    if (msg.type === "data" && msg.data) {
                        if (!authConfirmed) {
                            authConfirmed = true;
                            setConnecting(false);
                            setConnected(true);
                        }
                        xtermRef.current?.write(msg.data);
                    } else if (msg.type === "error") {
                        if (!authConfirmed) {
                            // Auth-phase error — show in the login form.
                            authErrorShown = true;
                            setConnecting(false);
                            setConnectError(
                                msg.error ?? t("sshErrorAuthFailed")
                            );
                        } else {
                            xtermRef.current?.writeln(
                                `\r\n\x1b[31mError: ${msg.error}\x1b[0m\r\n`
                            );
                        }
                    }
                } catch {
                    if (!authConfirmed) {
                        authConfirmed = true;
                        setConnecting(false);
                        setConnected(true);
                    }
                    xtermRef.current?.write(evt.data);
                }
            } else if (evt.data instanceof Blob) {
                evt.data.text().then((t) => {
                    if (!authConfirmed) {
                        authConfirmed = true;
                        setConnecting(false);
                        setConnected(true);
                    }
                    xtermRef.current?.write(t);
                });
            }
        };

        ws.onerror = () => {
            setConnecting(false);
            setConnected(false);
            setConnectError(t("sshErrorWebSocket"));
        };

        ws.onclose = (evt) => {
            setConnecting(false);
            if (authConfirmed) {
                setConnected(false);
                xtermRef.current?.writeln(
                    `\r\n\x1b[33mConnection closed (code ${evt.code})\x1b[0m\r\n`
                );
            }
            // If auth was never confirmed the login form is already visible;
            // a generic error is shown only when no specific error was received.
            if (!authConfirmed && !authErrorShown) {
                setConnectError(t("sshErrorConnectionClosed"));
            }
        };
    }

    function disconnect() {
        wsRef.current?.close();
        xtermRef.current?.dispose();
        xtermRef.current = null;
        setConnected(false);
    }

    // In push mode, show a connecting/connected state without the login form.
    if (signedKeyData && signedPrivateKey) {
        return (
            <>
                {!connected && (
                    <div className="flex items-center justify-center">
                        <Card className="w-full max-w-md">
                            <CardHeader>
                                <CardTitle>{t("sshTitle")}</CardTitle>
                                <CardDescription>
                                    {t("sshConnectingDescription")}
                                </CardDescription>
                            </CardHeader>
                            <CardContent className="flex flex-col items-center space-y-4">
                                {!connectError && (
                                    <div className="flex items-center space-x-2">
                                        <Loader2 className="h-5 w-5 animate-spin" />
                                        <span>
                                            {connecting
                                                ? t("sshConnecting")
                                                : t("sshInitializing")}
                                        </span>
                                    </div>
                                )}
                                {connectError && (
                                    <Alert
                                        variant="destructive"
                                        className="w-full"
                                    >
                                        <AlertCircle className="h-5 w-5" />
                                        <AlertDescription>
                                            {connectError}
                                        </AlertDescription>
                                    </Alert>
                                )}
                            </CardContent>
                        </Card>
                    </div>
                )}
                {connected && (
                    <div className="fixed inset-0 z-50 flex flex-col bg-neutral-900">
                        <div
                            ref={terminalRef}
                            className="flex-1 overflow-hidden"
                            style={{ minHeight: 0 }}
                        />
                    </div>
                )}
            </>
        );
    }

    if (error) {
        return (
            <div>
                <div className="text-center mb-2">
                    <span className="text-sm text-muted-foreground">
                        {t("sshPoweredBy")}{" "}
                        <Link
                            href="https://pangolin.net/"
                            target="_blank"
                            rel="noopener noreferrer"
                            className="underline"
                        >
                            Pangolin
                        </Link>
                    </span>
                </div>
                <Card className="w-full">
                    <CardHeader>
                        <CardTitle>{t("sshTitle")}</CardTitle>
                    </CardHeader>
                    <CardContent>
                        <p className="text-destructive text-sm">{error}</p>
                    </CardContent>
                </Card>
            </div>
        );
    }

    return (
        <>
            {!connected && (
                <div>
                    <div className="text-center mb-2">
                        <span className="text-sm text-muted-foreground">
                            {t("sshPoweredBy")}{" "}
                            <Link
                                href="https://pangolin.net/"
                                target="_blank"
                                rel="noopener noreferrer"
                                className="underline"
                            >
                                Pangolin
                            </Link>
                        </span>
                    </div>
                    <Card className="w-full">
                        <CardHeader>
                            <CardTitle>{t("sshSignInTitle")}</CardTitle>
                            <CardDescription>
                                {t("sshSignInDescription")}
                            </CardDescription>
                        </CardHeader>
                        <CardContent>
                            {/* Tab row */}
                            <div className="flex space-x-4 border-b mb-4">
                                {(["password", "privateKey"] as const).map(
                                    (tab) => (
                                        <button
                                            key={tab}
                                            type="button"
                                            onClick={() => setAuthTab(tab)}
                                            className={cn(
                                                "px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap relative",
                                                authTab === tab
                                                    ? "text-primary after:absolute after:bottom-0 after:left-0 after:right-0 after:h-0.5 after:bg-primary after:rounded-full"
                                                    : "text-muted-foreground hover:text-foreground"
                                            )}
                                        >
                                            {tab === "password"
                                                ? t("sshPasswordTab")
                                                : t("sshPrivateKeyTab")}
                                        </button>
                                    )
                                )}
                            </div>

                            {authTab === "password" && (
                                <div className="space-y-4">
                                    <Field
                                        label={t("username")}
                                        id="username-pw"
                                    >
                                        <Input
                                            id="username-pw"
                                            value={form.username}
                                            onChange={(e) =>
                                                setForm({
                                                    ...form,
                                                    username: e.target.value
                                                })
                                            }
                                            placeholder="root"
                                        />
                                    </Field>
                                    <Field label={t("password")} id="password">
                                        <Input
                                            id="password"
                                            type="password"
                                            value={form.password}
                                            onChange={(e) =>
                                                setForm({
                                                    ...form,
                                                    password: e.target.value
                                                })
                                            }
                                        />
                                    </Field>
                                </div>
                            )}

                            {authTab === "privateKey" && (
                                <div className="space-y-4">
                                    <p className="text-sm text-muted-foreground">
                                        {t("sshPrivateKeyDisclaimer")}{" "}
                                        <Link
                                            href="https://docs.pangolin.net/"
                                            target="_blank"
                                            rel="noopener noreferrer"
                                            className="underline inline-flex items-center gap-1"
                                        >
                                            {t("sshLearnMore")}
                                            <ExternalLink className="h-3 w-3" />
                                        </Link>
                                    </p>
                                    <Field
                                        label={t("username")}
                                        id="username-key"
                                    >
                                        <Input
                                            id="username-key"
                                            value={form.username}
                                            onChange={(e) =>
                                                setForm({
                                                    ...form,
                                                    username: e.target.value
                                                })
                                            }
                                            placeholder="root"
                                        />
                                    </Field>
                                    <Field
                                        label={t("sshPrivateKeyField")}
                                        id="privateKey"
                                    >
                                        <Textarea
                                            id="privateKey"
                                            value={form.privateKey}
                                            onChange={(e) =>
                                                setForm({
                                                    ...form,
                                                    privateKey: e.target.value
                                                })
                                            }
                                            placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
                                            rows={5}
                                            className="font-mono text-xs"
                                        />
                                    </Field>
                                    <Field
                                        label={t("sshPrivateKeyFile")}
                                        id="privateKeyFile"
                                    >
                                        <Input
                                            id="privateKeyFile"
                                            type="file"
                                            accept=".pem,.key,.pub,*"
                                            onChange={handleKeyFile}
                                        />
                                    </Field>
                                </div>
                            )}

                            <div className="mt-4 space-y-3">
                                {connectError && (
                                    <p className="text-destructive text-sm">
                                        {connectError}
                                    </p>
                                )}

                                <Button
                                    onClick={() => connect()}
                                    loading={connecting}
                                    disabled={
                                        !form.username ||
                                        (authTab === "password"
                                            ? !form.password
                                            : !form.privateKey)
                                    }
                                    className="w-full"
                                >
                                    {connecting
                                        ? t("sshConnecting")
                                        : t("sshAuthenticate")}
                                </Button>
                            </div>
                        </CardContent>
                    </Card>
                </div>
            )}

            {connected && (
                <div className="fixed inset-0 z-50 flex flex-col bg-neutral-900">
                    <div className="flex flex-wrap items-center gap-2 bg-black p-2 text-white">
                        <Button
                            size="sm"
                            variant="destructive"
                            onClick={disconnect}
                        >
                            {t("sshTerminate")}
                        </Button>
                    </div>
                    <div
                        ref={terminalRef}
                        className="flex-1 overflow-hidden"
                        style={{ minHeight: 0 }}
                    />
                </div>
            )}
        </>
    );
}

function Field({
    label,
    id,
    children
}: {
    label: string;
    id: string;
    children: React.ReactNode;
}) {
    return (
        <div className="space-y-1.5">
            <Label htmlFor={id}>{label}</Label>
            {children}
        </div>
    );
}
