import {
    AppShell,
    Group,
    Menu,
    Stack,
    Text,
    Title,
    UnstyledButton,
} from "@mantine/core";
import { Outlet, Link, createRootRoute, useLocation, useNavigate } from "@tanstack/react-router";
import { Check, ChevronDown, LogOut, Monitor, Moon, Settings, Sun, Wifi, WifiOff } from "lucide-react";
import { useEffect, useState, type ReactNode } from "react";
import { useAppStore, type ThemeMode } from "../store";
import { logout, refreshSessionScope } from "../features/auth/auth";
import { uploadClipboard } from "../features/clipboard/clipboard";
import { onNativeClipboard } from "../net/desktop";
import { startHub } from "../features/transfer/hub";
import { resyncPush } from "../net/push";
import { refreshICEServers, stopICEServerRefresh } from "../features/transfer/iceServers";
import { t, type Locale } from "../i18n";
import { Badge, Callout, IconButton, Tooltip } from "../ui/primitives";
import { Wordmark } from "../ui/brand";

export const Route = createRootRoute({ component: RootLayout });

function RootLayout()
{
    const user = useAppStore((s) => s.user);
    const authMode = useAppStore((s) => s.authMode);
    const sessionScope = useAppStore((s) => s.sessionScope);
    const selfDeviceName = useAppStore((s) => s.selfDeviceName);
    const sseConnected = useAppStore((s) => s.sseConnected);
    const sseReconnecting = useAppStore((s) => s.sseReconnecting);
    const locale = useAppStore((s) => s.locale);
    const setLocale = useAppStore((s) => s.setLocale);
    const theme = useAppStore((s) => s.theme);
    const setTheme = useAppStore((s) => s.setTheme);
    const navigate = useNavigate();
    const location = useLocation();
    // 登录 / 首次设置 / OAuth 回调使用全屏 AuthShell（由各路由自渲染），
    // 这里只渲染 <Outlet/>，不包 AppShell，从而隐去顶栏。
    const fullscreen = isAuthRoute(location.pathname);

    // 反应式登出守卫：当登录态在运行中被清空（access token 被远程吊销 / 过期 →
    // apiFetch 触发 forceLogout，见 net/api.ts），把界面送回登录页。路由的
    // beforeLoad 只在导航时跑，停在当前页时不会自动重定向，故这里补一道反应式跳转。
    // 仅 prod：dev 无远程吊销概念，登录态由 loginDev 同步建立。已在认证类路由上则
    // 不跳（避免与 /login 自身、扫码显码 / 批准页打架）。
    useEffect(() =>
    {
        if (authMode !== "prod") { return; }
        if (user) { return; }
        if (isAuthRoute(location.pathname)) { return; }
        navigate({ to: "/login", search: { dev_user: undefined } });
    }, [ authMode, user, location.pathname, navigate ]);

    // SSE 在登录后立即开启；user.id 或 selfDeviceName 任一变化即重连
    // （改名后会自动以新设备名重新注册）。
    useEffect(() =>
    {
        if (!user || !selfDeviceName) { return; }
        const ctrl = new AbortController();
        startHub(ctrl.signal);
        // 校正本会话权限级别（/api/me 的 scope）：受限访客据此隐藏账号管理入口。
        // 覆盖所有登录路径（OAuth / 扫码新设备 / 开机水合 / dev），一处搞定。
        void refreshSessionScope();
        // 预取 Cloudflare TURN 凭据（或 STUN 回退），首次 WebRTC 同步可用。
        void refreshICEServers();
        // 已开启推送的浏览器：登录后把订阅重新登记到当前用户名下（自愈换用户 /
        // endpoint 轮换 / locale 变更）；未开启或桌面端为 no-op。
        void resyncPush();
        // 桌面：本机复制 → 上传云端（复用 uploadClipboard 链路；浏览器里为 no-op）。
        // 上传失败静默——不打断用户，下次复制再试。
        const offNativeClipboard = onNativeClipboard((text) =>
        {
            void uploadClipboard(text).catch(() => { /* ignore */ });
        });
        return () =>
        {
            ctrl.abort();
            stopICEServerRefresh();
            offNativeClipboard();
        };
    }, [ user?.id, selfDeviceName ]);

    const handleLogout = () =>
    {
        logout();
        navigate({ to: "/login", search: { dev_user: undefined } });
    };

    if (fullscreen) { return <Outlet />; }

    return (
        <AppShell header={{ height: 56 }} padding="md">
            <AppShell.Header>
                <Group h="100%" px="md" justify="space-between" wrap="nowrap" gap="xs">
                    <Link
                        to="/"
                        style={{
                            textDecoration: "none",
                            color: "inherit",
                            minWidth: 0,
                            overflow: "hidden",
                            whiteSpace: "nowrap",
                            textOverflow: "ellipsis",
                        }}
                    >
                        <Title
                            order={4}
                            fw={600}
                            style={{ whiteSpace: "nowrap", fontSize: "var(--fs-22)" }}
                        >
                            <Wordmark />
                        </Title>
                    </Link>
                    {user && (
                        <Group gap={8} wrap="nowrap">
                            <ConnectionIcon connected={sseConnected} />
                            <ThemeQuickToggle theme={theme} onChange={setTheme} />
                            <Menu position="bottom-end" width={240} withArrow shadow="md">
                                <Menu.Target>
                                    <UnstyledButton
                                        aria-label={t("nav.accountMenu")}
                                        style={{
                                            display: "flex",
                                            alignItems: "center",
                                            gap: 6,
                                            padding: "4px 6px",
                                            borderRadius: 6,
                                        }}
                                    >
                                        <UserAvatar
                                            name={user.name}
                                            avatarUrl={user.avatar}
                                            size={28}
                                        />
                                        <Text size="sm" visibleFrom="sm" truncate maw={140}>
                                            {user.name}
                                        </Text>
                                        <ChevronDown size={14} />
                                    </UnstyledButton>
                                </Menu.Target>
                                <Menu.Dropdown>
                                    <Stack gap={2} px="sm" pt="xs" pb={6}>
                                        <Group gap={6} wrap="nowrap" align="center">
                                            <Text size="sm" fw={500} truncate>{user.name}</Text>
                                            {sessionScope === "guest" && (
                                                <Badge tone="warn">{t("settings.guest.badge")}</Badge>
                                            )}
                                        </Group>
                                        <Text size="xs" c="dimmed">
                                            {t("nav.thisDeviceLabel")}
                                            <Text span fw={500}>{selfDeviceName}</Text>
                                        </Text>
                                        <Text
                                            size="xs"
                                            style={{ color: sseConnected ? "var(--state-online)" : "var(--text-muted)" }}
                                        >
                                            {sseConnected
                                                ? `● ${t("app.connected")}`
                                                : `○ ${t("app.connecting")}`}
                                        </Text>
                                    </Stack>
                                    <Menu.Divider />
                                    <Menu.Label>{t("nav.theme")}</Menu.Label>
                                    {/* System 列于最上 — 默认跟随系统，最常用。 */}
                                    <ThemeItem
                                        mode="system"
                                        label={t("nav.theme.system")}
                                        icon={<Monitor size={14} />}
                                        current={theme}
                                        onSelect={setTheme}
                                    />
                                    <ThemeItem
                                        mode="light"
                                        label={t("nav.theme.light")}
                                        icon={<Sun size={14} />}
                                        current={theme}
                                        onSelect={setTheme}
                                    />
                                    <ThemeItem
                                        mode="dark"
                                        label={t("nav.theme.dark")}
                                        icon={<Moon size={14} />}
                                        current={theme}
                                        onSelect={setTheme}
                                    />
                                    <Menu.Divider />
                                    <Menu.Item
                                        leftSection={<Settings size={14} />}
                                        onClick={() => navigate({ to: "/settings" })}
                                    >
                                        {t("nav.settings")}
                                    </Menu.Item>
                                    <Menu.Divider />
                                    <Menu.Label>{t("nav.language")}</Menu.Label>
                                    <LocaleItem
                                        locale="zh-CN"
                                        label="简体中文"
                                        current={locale}
                                        onSelect={setLocale}
                                    />
                                    <LocaleItem
                                        locale="zh-TW"
                                        label="繁體中文"
                                        current={locale}
                                        onSelect={setLocale}
                                    />
                                    <LocaleItem
                                        locale="en-US"
                                        label="English"
                                        current={locale}
                                        onSelect={setLocale}
                                    />
                                    <Menu.Divider />
                                    <Menu.Item
                                        color="red"
                                        leftSection={<LogOut size={14} />}
                                        onClick={handleLogout}
                                    >
                                        {t("nav.signOut")}
                                    </Menu.Item>
                                </Menu.Dropdown>
                            </Menu>
                        </Group>
                    )}
                </Group>
            </AppShell.Header>
            <AppShell.Main>
                {sseReconnecting && (
                    <div style={{ marginBottom: "var(--space-4)" }}>
                        <Callout
                            tone="error"
                            icon={<WifiOff size={16} />}
                            title={t("app.disconnectedTitle")}
                        >
                            {t("app.disconnectedHint")}
                        </Callout>
                    </div>
                )}
                <Outlet />
            </AppShell.Main>
        </AppShell>
    );
}

// Header 上的快捷按钮：单击在三态间循环 system → light → dark → system。
// 图标随当前模式切换；hover 显示 tooltip 提示下一步会切到什么。
function ThemeQuickToggle(props: { theme: ThemeMode; onChange: (m: ThemeMode) => void })
{
    const { theme, onChange } = props;
    const next: ThemeMode = theme === "system" ? "light" : theme === "light" ? "dark" : "system";
    const icon = theme === "light" ? <Sun size={16} />
               : theme === "dark"  ? <Moon size={16} />
               : <Monitor size={16} />;
    const label = `${t("nav.themeToggle")}（${t(`nav.theme.${theme}` as const)} → ${t(`nav.theme.${next}` as const)}）`;
    return (
        <Tooltip label={label}>
            <IconButton
                aria-label={label}
                size="sm"
                onClick={() => onChange(next)}
            >
                {icon}
            </IconButton>
        </Tooltip>
    );
}

function ThemeItem(props: {
    mode: ThemeMode;
    label: string;
    icon: ReactNode;
    current: ThemeMode;
    onSelect: (m: ThemeMode) => void;
})
{
    const { mode, label, icon, current, onSelect } = props;
    const active = mode === current;
    return (
        <Menu.Item
            leftSection={active
                ? <Check size={14} />
                : <span style={{ display: "inline-block", width: 14 }} />}
            rightSection={icon}
            onClick={() => { if (!active) { onSelect(mode); } }}
        >
            {label}
        </Menu.Item>
    );
}

function LocaleItem(props: {
    locale: Locale;
    label: string;
    current: Locale;
    onSelect: (l: Locale) => void;
})
{
    const { locale, label, current, onSelect } = props;
    const active = locale === current;
    return (
        <Menu.Item
            leftSection={active
                ? <Check size={14} />
                : <span style={{ display: "inline-block", width: 14 }} />}
            onClick={() => { if (!active) { onSelect(locale); } }}
        >
            {label}
        </Menu.Item>
    );
}

function ConnectionIcon(props: { connected: boolean })
{
    const { connected } = props;
    const color = connected ? "var(--state-online)" : "var(--text-muted)";
    const label = connected ? t("app.connected") : t("app.connecting");
    return (
        <span
            aria-label={label}
            title={label}
            style={{ display: "inline-flex", alignItems: "center" }}
        >
            {connected
                ? <Wifi size={16} color={color} />
                : <WifiOff size={16} color={color} />}
        </span>
    );
}

// 这些路由自己撑满 viewport（各自渲染 AuthShell），root layout 不再套 AppShell，
// 否则会出现 AppShell 顶栏 + AuthShell 全屏壳的双层 chrome。扫码登录的显码页 /
// 批准页同属此列。
function isAuthRoute(pathname: string): boolean
{
    return pathname === "/login"
        || pathname === "/setup"
        || pathname === "/link"
        || pathname === "/link/new"
        || pathname === "/link/scan"
        || pathname.startsWith("/oauth/");
}

function initial(name: string): string
{
    const trimmed = name.trim();
    if (!trimmed) { return "?"; }
    // 取首个码点（兼容 emoji / 多字节中文姓名）。
    const first = Array.from(trimmed)[0];
    return first.toUpperCase();
}

// 头像：优先 Casdoor 提供的图（user.avatar）。URL 缺失或加载失败时回退到
// 字母方块 —— 浅色主题白底黑字、深色主题黑底白字（与 Dracula 调色对比最强）。
function UserAvatar(props: { name: string; avatarUrl?: string; size?: number })
{
    const { name, avatarUrl, size = 28 } = props;
    const [ broken, setBroken ] = useState(false);
    const fontSize = Math.round(size * 0.42);

    const baseStyle: React.CSSProperties = {
        width:        size,
        height:       size,
        borderRadius: "50%",
        display:      "inline-flex",
        alignItems:   "center",
        justifyContent: "center",
        overflow:     "hidden",
        flexShrink:   0,
        userSelect:   "none",
    };

    if (avatarUrl && !broken)
    {
        return (
            <img
                src={avatarUrl}
                alt=""
                referrerPolicy="no-referrer"
                onError={() => setBroken(true)}
                style={{ ...baseStyle, objectFit: "cover" }}
            />
        );
    }

    // 字母方块：颜色由语义令牌驱动，主题切换时自然反转。
    const letterStyle: React.CSSProperties = {
        ...baseStyle,
        background: "var(--avatar-bg)",
        color: "var(--avatar-fg)",
        fontFamily: "var(--font-sans)",
        fontWeight: 600,
        fontSize,
        lineHeight: 1,
        letterSpacing: 0,
    };
    return <span style={letterStyle} aria-label={name}>{initial(name)}</span>;
}
