<script lang="ts">
    import { onMount } from "svelte";
    import { EVENT_LAYOUT_READY, EVENT_REQUEST_OPEN_P2P_SETTINGS, eventHub } from "@/common/events";
    import {
        EVENT_SERVER_STATUS,
        EVENT_REQUEST_STATUS,
        EVENT_P2P_REPLICATOR_STATUS,
        EVENT_P2P_REPLICATOR_PROGRESS,
        type P2PServerInfo,
    } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/TrysteroReplicatorP2PServer";
    import type { P2PServiceViews } from "@vrtmrz/livesync-commonlib/p2p";
    import type { P2PReplicatorStatus, P2PReplicationReport } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/TrysteroReplicator";
    import { delay, fireAndForget } from "octagonal-wheels/promises";
    import P2PServerStatusCard from "./P2PServerStatusCard.svelte";
    import { EVENT_SETTING_SAVED } from "@vrtmrz/livesync-commonlib/compat/events/coreEvents";
    import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore";
    import { ConnectionStringParser } from "@vrtmrz/livesync-commonlib/compat/common/ConnectionString";
    import type { P2PSyncSetting } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.type";
    import {
        activateP2PRemoteConfiguration,
        createRemoteConfigurationId,
        type RemoteConfiguration,
    } from "@vrtmrz/livesync-commonlib/remote-configurations";
    import { extractP2PRoomSuffix } from "@vrtmrz/livesync-commonlib/compat/common/utils";
    import { SetupManager } from "@/modules/features/SetupManager";
    import { Menu } from "@/deps";
    import { $msg as translateMessage } from "@/common/translation";
    import {
        hasExactP2PPeer,
        togglePersistedP2PPeer,
        type PersistedP2PPeerSetting,
    } from "./p2pPeerSettings";

    interface Props {
        p2p: P2PServiceViews;
        core: LiveSyncBaseCore;
    }

    let { p2p, core }: Props = $props();
    let serverInfo = $state<P2PServerInfo | undefined>(undefined);
    let replicatorInfo = $state<P2PReplicatorStatus | undefined>(undefined);
    let decidingPeerId = $state<string | null>(null);
    let replicatingPeerId = $state<string | null>(null);
    let communicatingUntil = $state<Record<string, number>>({});
    const COMMUNICATION_HOLD_MS = 2500;
    // Later setting changes arrive through EVENT_SETTING_SAVED; these values only seed local state at mount time.
    const readCurrentSettings = () => core.services.setting.currentSettings();
    const initialSettings = readCurrentSettings();
    type P2PRemoteOption = {
        id: string;
        name: string;
        roomSuffix: string;
    };
    let p2pRemoteOptions = $state<P2PRemoteOption[]>([]);
    let selectedP2PRemoteConfigurationId = $state(initialSettings?.P2P_ActiveRemoteConfigurationId ?? "");
    let selectingP2PRemote = $state(false);

    let peerMenu: Menu | undefined;

    function markCommunicating(peerId: string) {
        const expiry = Date.now() + COMMUNICATION_HOLD_MS;
        communicatingUntil = { ...communicatingUntil, [peerId]: expiry };
        window.setTimeout(() => {
            if ((communicatingUntil[peerId] ?? 0) <= Date.now()) {
                const { [peerId]: _removed, ...rest } = communicatingUntil;
                communicatingUntil = rest;
            }
        }, COMMUNICATION_HOLD_MS + 100);
    }

    function listP2PRemoteOptions(
        remoteConfigurations: Record<string, RemoteConfiguration> | undefined
    ): P2PRemoteOption[] {
        return Object.values(remoteConfigurations ?? {})
            .map((config) => {
                try {
                    const parsed = ConnectionStringParser.parse(config.uri);
                    if (parsed.type !== "p2p") {
                        return undefined;
                    }
                    return {
                        id: config.id,
                        name: config.name,
                        roomSuffix: extractP2PRoomSuffix(parsed.settings.P2P_roomID ?? ""),
                    } as P2PRemoteOption;
                } catch {
                    return undefined;
                }
            })
            .filter((e): e is P2PRemoteOption => !!e);
    }

    function refreshP2PRemoteOptions() {
        const settings = core.services.setting.currentSettings();
        const options = listP2PRemoteOptions(settings.remoteConfigurations);
        p2pRemoteOptions = options;
        const currentSelected = settings.P2P_ActiveRemoteConfigurationId ?? "";
        const isCurrentSelectedValid = options.some((option) => option.id === currentSelected);
        if (options.length === 0) {
            selectedP2PRemoteConfigurationId = "";
            return;
        }
        if (currentSelected.trim() === "" || !isCurrentSelectedValid) {
            const fallbackId = options[0].id;
            selectedP2PRemoteConfigurationId = fallbackId;
            if (currentSelected !== fallbackId) {
                fireAndForget(() => applyP2PActiveRemoteSelection(fallbackId));
            }
            return;
        }
        selectedP2PRemoteConfigurationId = currentSelected;
    }

    function canEditP2PSettings() {
        const selected = selectedP2PRemoteConfigurationId.trim();
        if (selected === "") {
            return false;
        }
        return p2pRemoteOptions.some((e) => e.id === selected);
    }

    async function requestServerStatus() {
        p2p.diagnostics.requestStatus();
        eventHub.emitEvent(EVENT_REQUEST_STATUS);
    }

    onMount(() => {
        const unsubscribe = eventHub.onEvent(EVENT_SERVER_STATUS, (status) => {
            serverInfo = status;
        });
        const unsubscribeReplicatorStatus = eventHub.onEvent(EVENT_P2P_REPLICATOR_STATUS, (status) => {
            replicatorInfo = status;
            for (const peerId of status.replicatingFrom) {
                markCommunicating(peerId);
            }
            for (const peerId of status.replicatingTo) {
                markCommunicating(peerId);
            }
        });
        const unsubscribeReplicatorProgress = eventHub.onEvent(EVENT_P2P_REPLICATOR_PROGRESS, (report) => {
            const rep = report as P2PReplicationReport;
            if (("fetching" in rep && rep.fetching?.isActive) || ("sending" in rep && rep.sending?.isActive)) {
                markCommunicating(rep.peerId);
            }
        });

        const unsubscribeSettings = eventHub.onEvent(EVENT_SETTING_SAVED, (settings) => {
            refreshP2PRemoteOptions();
        });
        const unsubscribeLayoutReady = eventHub.onEvent(EVENT_LAYOUT_READY, () => {
            refreshP2PRemoteOptions();
            void requestServerStatus();
        });

        fireAndForget(async () => {
            await delay(100);
            refreshP2PRemoteOptions();
            await requestServerStatus();
        });

        return () => {
            unsubscribe();
            unsubscribeReplicatorStatus();
            unsubscribeReplicatorProgress();
            unsubscribeSettings();
            unsubscribeLayoutReady();
            peerMenu?.hide();
        };
    });

    function getAcceptanceStatus(peer: P2PServerInfo["knownAdvertisements"][number]) {
        if (peer.isTemporaryAccepted === true) return translateMessage("ACCEPTED (in session)");
        if (peer.isAccepted === true) return translateMessage("ACCEPTED");
        if (peer.isTemporaryAccepted === false) return translateMessage("DENIED (in session)");
        if (peer.isAccepted === false) return translateMessage("DENIED");
        return translateMessage("NEW");
    }

    function getAcceptanceStatusClass(peer: P2PServerInfo["knownAdvertisements"][number]) {
        if (peer.isTemporaryAccepted === true || peer.isAccepted === true) return "accepted";
        if (peer.isTemporaryAccepted === false || peer.isAccepted === false) return "denied";
        return "unknown";
    }

    function openConnectionSettings() {
        eventHub.emitEvent(EVENT_REQUEST_OPEN_P2P_SETTINGS);
    }

    async function applyP2PActiveRemoteSelection(id: string) {
        selectingP2PRemote = true;
        try {
            await core.services.setting.updateSettings((settings) => {
                settings.P2P_ActiveRemoteConfigurationId = id;
                if (id.trim() === "") {
                    return settings;
                }
                const activated = activateP2PRemoteConfiguration(settings, id);
                return activated || settings;
            }, true);
            refreshP2PRemoteOptions();
        } finally {
            selectingP2PRemote = false;
        }
    }

    async function onP2PRemoteSelected(event: Event) {
        const target = event.currentTarget as HTMLSelectElement;
        const id = target.value;
        selectedP2PRemoteConfigurationId = id;
        await applyP2PActiveRemoteSelection(id);
    }

    async function createAndSelectP2PRemote() {
        const setupManager = core.getModule(SetupManager);
        const currentSettings = core.services.setting.currentSettings();
        const p2pConf = await setupManager.openP2PSetup(currentSettings);
        if (p2pConf === "cancelled" || typeof p2pConf !== "object" || !p2pConf) {
            return;
        }
        const p2pSettings = p2pConf as Partial<P2PSyncSetting>;
        const id = createRemoteConfigurationId();
        const roomSuffix = extractP2PRoomSuffix(p2pSettings.P2P_roomID ?? "");
        const name = roomSuffix ? `P2P Remote (${roomSuffix})` : "P2P Remote";
        await core.services.setting.updateSettings((settings) => {
            const merged = {
                ...settings,
                ...p2pSettings,
            };
            const uri = ConnectionStringParser.serialize({ type: "p2p", settings: merged });
            settings.remoteConfigurations = {
                ...(settings.remoteConfigurations ?? {}),
                [id]: {
                    id,
                    name,
                    uri,
                    isEncrypted: false,
                },
            };
            settings.P2P_ActiveRemoteConfigurationId = id;
            const activated = activateP2PRemoteConfiguration(settings, id);
            return activated || settings;
        }, true);
        refreshP2PRemoteOptions();
    }

    async function updateSelectedP2PRemote(partial: Partial<P2PSyncSetting>) {
        const selectedId = core.services.setting.currentSettings().P2P_ActiveRemoteConfigurationId?.trim() ?? "";
        if (selectedId === "") {
            return;
        }
        await core.services.setting.updateSettings((settings) => {
            const config = settings.remoteConfigurations?.[selectedId];
            if (!config) {
                return settings;
            }
            let parsed;
            try {
                parsed = ConnectionStringParser.parse(config.uri);
            } catch {
                return settings;
            }
            if (parsed.type !== "p2p") {
                return settings;
            }
            const mergedP2P = {
                ...parsed.settings,
                ...partial,
            };
            const uri = ConnectionStringParser.serialize({
                type: "p2p",
                settings: {
                    ...settings,
                    ...mergedP2P,
                },
            });
            settings.remoteConfigurations = {
                ...(settings.remoteConfigurations ?? {}),
                [selectedId]: {
                    ...config,
                    uri,
                    isEncrypted: false,
                },
            };
            Object.assign(settings, partial);
            const activated = activateP2PRemoteConfiguration(settings, selectedId);
            return activated || settings;
        }, true);
    }

    async function makeDecision(
        peer: P2PServerInfo["knownAdvertisements"][number],
        decision: boolean,
        isTemporary: boolean
    ) {
        decidingPeerId = peer.peerId;
        try {
            await p2p.peerAdmission.makeDecision({
                peerId: peer.peerId,
                name: peer.name,
                decision,
                isTemporary,
            });
            await requestServerStatus();
        } finally {
            decidingPeerId = null;
        }
    }

    async function revokeDecision(peer: P2PServerInfo["knownAdvertisements"][number]) {
        decidingPeerId = peer.peerId;
        try {
            await p2p.peerAdmission.revokeDecision({
                peerId: peer.peerId,
                name: peer.name,
            });
            await requestServerStatus();
        } finally {
            decidingPeerId = null;
        }
    }

    async function startReplication(peer: P2PServerInfo["knownAdvertisements"][number]) {
        replicatingPeerId = peer.peerId;
        try {
            await p2p.targetedTransfer.synchroniseWithPeer(peer.peerId, true);
            await requestServerStatus();
        } finally {
            replicatingPeerId = null;
        }
    }

    function isAccepted(peer: P2PServerInfo["knownAdvertisements"][number]) {
        return peer.isTemporaryAccepted === true || peer.isAccepted === true;
    }

    function isWatching(peerId: string) {
        return replicatorInfo?.watchingPeers?.includes(peerId) ?? false;
    }

    function toggleWatch(peerId: string) {
        if (!canEditP2PSettings()) {
            return;
        }
        if (isWatching(peerId)) {
            p2p.changeRelay.unwatchPeer(peerId);
        } else {
            p2p.changeRelay.watchPeer(peerId);
        }
    }

    function isCommunicating(peerId: string) {
        const to = replicatorInfo?.replicatingTo ?? [];
        const from = replicatorInfo?.replicatingFrom ?? [];
        const isLiveCommunicating = to.includes(peerId) || from.includes(peerId);
        const isHeldCommunicating = (communicatingUntil[peerId] ?? 0) > Date.now();
        return isLiveCommunicating || isHeldCommunicating;
    }

    function isPersistedPeerSettingEnabled(setting: PersistedP2PPeerSetting, peerName: string) {
        const settings = core.services.setting.currentSettings();
        return hasExactP2PPeer(settings[setting] ?? "", peerName);
    }

    async function togglePersistedPeerSetting(
        peer: P2PServerInfo["knownAdvertisements"][number],
        setting: PersistedP2PPeerSetting
    ) {
        if (!canEditP2PSettings()) {
            return;
        }
        const currentSettings = core.services.setting.currentSettings();
        await updateSelectedP2PRemote(togglePersistedP2PPeer(currentSettings, setting, peer.name));
    }

    function openPeerMenu(event: MouseEvent, peer: P2PServerInfo["knownAdvertisements"][number]) {
        peerMenu?.hide();
        peerMenu = new Menu()
            .addItem((item) => {
                item.setTitle(translateMessage("Synchronise when this device connects"))
                    .setChecked(isPersistedPeerSettingEnabled("P2P_AutoSyncPeers", peer.name))
                    .onClick(() => {
                        void togglePersistedPeerSetting(peer, "P2P_AutoSyncPeers");
                    });
            })
            .addItem((item) => {
                item.setTitle(translateMessage("Follow whenever this device connects"))
                    .setChecked(isPersistedPeerSettingEnabled("P2P_AutoWatchPeers", peer.name))
                    .onClick(() => {
                        void togglePersistedPeerSetting(peer, "P2P_AutoWatchPeers");
                    });
            })
            .addItem((item) => {
                item.setTitle(translateMessage("Include in the P2P synchronisation command"))
                    .setChecked(isPersistedPeerSettingEnabled("P2P_SyncOnReplication", peer.name))
                    .onClick(() => {
                        void togglePersistedPeerSetting(peer, "P2P_SyncOnReplication");
                    });
            });
        const target = event.currentTarget as HTMLElement;
        const rect = target.getBoundingClientRect();
        peerMenu.showAtPosition({ x: rect.left, y: rect.bottom });
    }
</script>

<div class="p2p-container">
    <div class="pane-header">
        <h2>{translateMessage("P2P Status")}</h2>
        <div class="pane-header-actions">
            <div class="remote-picker-wrap">
                <select
                    class="remote-picker"
                    value={selectedP2PRemoteConfigurationId}
                    onchange={onP2PRemoteSelected}
                    disabled={selectingP2PRemote}
                    aria-label={translateMessage("Select active P2P remote")}
                    title={translateMessage("Select active P2P remote")}
                >
                    {#if p2pRemoteOptions.length === 0}
                        <option value="">{translateMessage("Select P2P remote...")}</option>
                    {/if}
                    {#each p2pRemoteOptions as option}
                        <option value={option.id}>
                            {option.name}{option.roomSuffix ? ` (${option.roomSuffix})` : ""}
                        </option>
                    {/each}
                </select>
                <button
                    class="icon-button"
                    onclick={() => createAndSelectP2PRemote()}
                    title={translateMessage("Create P2P remote")}
                    aria-label={translateMessage("Create P2P remote")}
                >
                    +
                </button>
            </div>
            <button
                class="icon-button"
                onclick={openConnectionSettings}
                title={translateMessage("Open P2P Setup...")}
                aria-label={translateMessage("Open P2P Setup...")}
            >
                ⚙
            </button>
        </div>
    </div>

    {#if !canEditP2PSettings()}
        <p class="warning-line">
            {translateMessage("Please select an active P2P remote configuration to change P2P sync targets.")}
        </p>
    {/if}

    <P2PServerStatusCard {p2p} {core} />

    <div class="peers-section">
        <div class="peers-header">
            <h3>{translateMessage("Detected Peers")}</h3>
            <button class="refresh" onclick={requestServerStatus}>{translateMessage("Refresh")}</button>
        </div>

        {#if serverInfo && serverInfo.knownAdvertisements.length > 0}
            <div class="peers-list">
                {#each serverInfo.knownAdvertisements as peer (peer.peerId)}
                    <div class="peer-item">
                        <div class="peer-info">
                            <div class="peer-name">
                                {peer.name} :
                                <span class="peer-id-mini" title={peer.peerId}>({peer.peerId.slice(0, 8)})</span>
                                {#if isCommunicating(peer.peerId)}
                                    <span
                                        class="comm-icon"
                                        title={translateMessage("Communicating")}
                                        aria-label={translateMessage("Communicating")}>📡</span
                                    >
                                {/if}
                            </div>
                            <div class="peer-meta">
                                <span class="badge">{peer.platform}</span>
                            </div>
                        </div>
                        <div class="peer-actions">
                            {#if isAccepted(peer)}
                                <div class="decision-row accepted-row">
                                    <span class="badge status-chip {getAcceptanceStatusClass(peer)}">
                                        {getAcceptanceStatus(peer)}
                                    </span>
                                    <button
                                        class="emoji-button"
                                        disabled={replicatingPeerId !== null}
                                        title={replicatingPeerId === peer.peerId
                                            ? translateMessage("Replicating...")
                                            : translateMessage("Replicate now")}
                                        aria-label={replicatingPeerId === peer.peerId
                                            ? translateMessage("Replicating")
                                            : translateMessage("Replicate now")}
                                        onclick={() => startReplication(peer)}
                                    >
                                        {replicatingPeerId === peer.peerId ? "⏳" : "🔄"}
                                    </button>
                                    <button
                                        class="action-button"
                                        disabled={decidingPeerId !== null}
                                        onclick={() => revokeDecision(peer)}
                                    >
                                        {translateMessage("Revoke")}
                                    </button>
                                    <button
                                        class="emoji-button"
                                        title={translateMessage("More actions for ${DEVICE}", { DEVICE: peer.name })}
                                        aria-label={translateMessage("More actions for ${DEVICE}", {
                                            DEVICE: peer.name,
                                        })}
                                        disabled={!canEditP2PSettings()}
                                        onclick={(event) => openPeerMenu(event, peer)}
                                    >
                                        …
                                    </button>
                                </div>
                                <div class="decision-row watch-row">
                                    <span class="decision-label">{translateMessage("Follow changes")}</span>
                                    <button
                                        class="emoji-button {isWatching(peer.peerId) ? 'is-watching' : ''}"
                                        title={isWatching(peer.peerId)
                                            ? translateMessage("Stop following changes from this device")
                                            : translateMessage("Follow changes from this device")}
                                        aria-label={isWatching(peer.peerId)
                                            ? translateMessage("Stop following changes from this device")
                                            : translateMessage("Follow changes from this device")}
                                        disabled={!canEditP2PSettings()}
                                        onclick={() => toggleWatch(peer.peerId)}
                                    >
                                        {isWatching(peer.peerId) ? "🔔" : "🔕"}
                                    </button>
                                </div>
                            {:else}
                                <div class="decision-status">
                                    <span class="badge status-chip {getAcceptanceStatusClass(peer)}">
                                        {getAcceptanceStatus(peer)}
                                    </span>
                                </div>
                                <div class="decision-row">
                                    <span class="decision-label">{translateMessage("PERMANENT")}</span>
                                    <button
                                        class="emoji-button"
                                        title={translateMessage("Allow permanently")}
                                        aria-label={translateMessage("Allow permanently")}
                                        disabled={decidingPeerId !== null}
                                        onclick={() => makeDecision(peer, true, false)}
                                    >
                                        ✅
                                    </button>
                                    <button
                                        class="emoji-button mod-warning"
                                        title={translateMessage("Deny permanently")}
                                        aria-label={translateMessage("Deny permanently")}
                                        disabled={decidingPeerId !== null}
                                        onclick={() => makeDecision(peer, false, false)}
                                    >
                                        🚫
                                    </button>
                                </div>
                                <div class="decision-row">
                                    <span class="decision-label">{translateMessage("SESSION")}</span>
                                    <button
                                        class="emoji-button"
                                        title={translateMessage("Allow in session")}
                                        aria-label={translateMessage("Allow in session")}
                                        disabled={decidingPeerId !== null}
                                        onclick={() => makeDecision(peer, true, true)}
                                    >
                                        ✅
                                    </button>
                                    <button
                                        class="emoji-button mod-warning"
                                        title={translateMessage("Deny in session")}
                                        aria-label={translateMessage("Deny in session")}
                                        disabled={decidingPeerId !== null}
                                        onclick={() => makeDecision(peer, false, true)}
                                    >
                                        🚫
                                    </button>
                                </div>
                            {/if}
                            {#if !isAccepted(peer) && (peer.isAccepted !== undefined || peer.isTemporaryAccepted !== undefined)}
                                <button
                                    class="action-button revoke-inline"
                                    disabled={decidingPeerId !== null}
                                    onclick={() => revokeDecision(peer)}
                                >
                                    {translateMessage("Revoke")}
                                </button>
                            {/if}
                        </div>
                    </div>
                {/each}
            </div>
        {:else if serverInfo}
            <p class="no-peers">
                {translateMessage("No devices available. Waiting for other devices to connect...")}
            </p>
        {:else}
            <p class="no-peers">{translateMessage("Fetching status...")}</p>
        {/if}
    </div>
</div>

<style>
    .p2p-container {
        display: flex;
        flex-direction: column;
        gap: 1rem;
    }

    .peers-section {
        border: 1px solid var(--divider-color);
        border-radius: 0.5rem;
        padding: 1rem;
    }

    .pane-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    .pane-header-actions {
        display: flex;
        align-items: center;
        gap: 0.4rem;
        min-width: 0;
    }

    .remote-picker-wrap {
        display: inline-flex;
        gap: 0.3rem;
        align-items: center;
        min-width: 0;
    }

    .remote-picker {
        max-width: 10rem;
        min-width: 1em;
        flex-shrink: 1;
        height: 1.9rem;
        border: 1px solid var(--divider-color);
        border-radius: 0.4rem;
        background-color: var(--interactive-normal);
        color: var(--text-normal);
        padding: 0 0.45rem;
    }

    .warning-line {
        margin: -0.2rem 0 0;
        font-size: 0.82rem;
        color: var(--text-warning);
    }

    .pane-header h2 {
        margin: 0;
        font-size: 1.1rem;
        font-weight: 700;
        white-space: nowrap;
    }

    .icon-button {
        width: 1.9rem;
        height: 1.9rem;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-size: 1rem;
        line-height: 1;
        border: 1px solid var(--divider-color);
        border-radius: 0.4rem;
        background-color: var(--interactive-normal);
        color: var(--text-normal);
        cursor: pointer;
        flex-shrink: 0;
    }

    .icon-button:hover {
        background-color: var(--interactive-hover);
    }

    .peers-header {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
        align-items: center;
        gap: 0.5rem;
        margin-bottom: 0.75rem;
    }

    h3 {
        margin: 0;
        font-weight: 600;
        font-size: 1rem;
    }

    .refresh {
        font-size: 0.8rem;
        padding: 0.2rem 0.5rem;
        border: 1px solid var(--divider-color);
        border-radius: 0.3rem;
        background-color: var(--interactive-normal);
        color: var(--text-normal);
        cursor: pointer;
    }

    .refresh:hover {
        background-color: var(--interactive-hover);
    }

    .peers-list {
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
    }

    .peer-item {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 0.75rem;
        padding: 0.75rem;
        background-color: var(--background-secondary);
        border: 1px solid var(--divider-color);
        border-radius: 0.4rem;
    }

    .peer-info {
        flex: 1;
        min-width: 0;
    }

    .peer-name {
        font-weight: 600;
        margin-bottom: 0.25rem;
        font-size: 0.9rem;
        display: flex;
        align-items: center;
        gap: 0.35rem;
    }

    .peer-meta {
        display: flex;
        gap: 0.5rem;
        font-size: 0.8rem;
        flex-wrap: wrap;
    }

    .badge {
        background-color: var(--background-tertiary);
        padding: 0.1rem 0.4rem;
        border-radius: 0.25rem;
    }

    .status-chip {
        font-weight: 600;
    }

    .status-chip.accepted {
        background-color: var(--background-modifier-success);
        color: var(--text-normal);
    }

    .status-chip.denied {
        background-color: var(--background-modifier-error);
        color: var(--text-normal);
    }

    .status-chip.unknown {
        background-color: var(--background-modifier-border);
        color: var(--text-muted);
    }

    .peer-id-mini {
        font-family: monospace;
        color: var(--text-muted);
        font-size: 0.75rem;
    }

    .comm-icon {
        font-size: 0.8rem;
        line-height: 1;
        animation: pulse-comm 1.2s ease-in-out infinite;
    }

    @keyframes pulse-comm {
        0% {
            opacity: 0.55;
            transform: scale(0.95);
        }
        50% {
            opacity: 1;
            transform: scale(1);
        }
        100% {
            opacity: 0.55;
            transform: scale(0.95);
        }
    }

    .peer-actions {
        display: flex;
        flex-direction: column;
        gap: 0.35rem;
        width: 100%;
        min-width: 0;
    }

    .decision-status {
        display: flex;
        justify-content: flex-start;
    }

    .decision-row {
        display: grid;
        grid-template-columns: 1fr auto auto;
        align-items: center;
        gap: 0.35rem;
    }

    .accepted-row {
        grid-template-columns: 1fr auto auto auto;
    }

    .decision-label {
        font-size: 0.7rem;
        font-weight: 700;
        color: var(--text-muted);
        letter-spacing: 0.03em;
    }

    .action-button {
        font-size: 0.75rem;
        padding: 0.2rem 0.45rem;
        border: 1px solid var(--divider-color);
        border-radius: 0.3rem;
        background-color: var(--interactive-normal);
        color: var(--text-normal);
        cursor: pointer;
        width: auto;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .emoji-button {
        width: 2rem;
        height: 1.7rem;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border: 1px solid var(--divider-color);
        border-radius: 0.3rem;
        background-color: var(--interactive-normal);
        cursor: pointer;
        padding: 0;
        line-height: 1;
    }

    .emoji-button.mod-warning {
        background-color: var(--background-modifier-error);
    }

    .emoji-button.is-watching {
        background-color: var(--interactive-accent);
        color: var(--text-on-accent);
        border-color: var(--interactive-accent);
    }

    .emoji-button:hover:not(:disabled) {
        background-color: var(--interactive-hover);
    }

    .emoji-button.mod-warning:hover:not(:disabled) {
        filter: brightness(0.95);
    }

    .watch-row {
        margin-top: 0.25rem;
    }

    .action-button:hover:not(:disabled) {
        background-color: var(--interactive-hover);
    }

    .action-button.mod-warning {
        background-color: var(--background-modifier-error);
    }

    .action-button:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .emoji-button:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .revoke-inline {
        justify-self: start;
    }

    .no-peers {
        text-align: center;
        color: var(--text-muted);
        font-size: 0.9rem;
        padding: 1rem;
    }
</style>
