import { eventHub, EVENT_REQUEST_OPEN_P2P } from "@/common/events";
import type { NecessaryServices } from "@vrtmrz/livesync-commonlib/compat/interfaces/ServiceModule";
import { type UseP2PReplicatorResult } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/UseP2PReplicatorResult";
import {
    P2PServerStatusPaneView,
    VIEW_TYPE_P2P_SERVER_STATUS,
} from "@/features/P2PSync/P2PReplicator/P2PServerStatusPaneView";
import type { LiveSyncCore } from "@/main";
import type { WorkspaceLeaf } from "@/deps";
import { REMOTE_P2P } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const";
import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.type";
import { ConnectionStringParser } from "@vrtmrz/livesync-commonlib/compat/common/ConnectionString";

export const LEGACY_VIEW_TYPE_P2P = "p2p-replicator";

class LegacyP2PStatusPaneView extends P2PServerStatusPaneView {
    override getViewType() {
        return LEGACY_VIEW_TYPE_P2P;
    }
}

/** Host-owned peer-selection entry used by the two adjunct P2P commands. */
export type OpenInteractiveP2PReplication = (showResult: boolean) => Promise<boolean | void>;

export function hasP2PConfiguration(settings: Partial<ObsidianLiveSyncSettings>): boolean {
    if (
        settings.remoteType === REMOTE_P2P ||
        settings.P2P_Enabled === true ||
        (settings.P2P_roomID ?? "").trim() !== "" ||
        (settings.P2P_passphrase ?? "").trim() !== ""
    ) {
        return true;
    }
    return Object.values(settings.remoteConfigurations ?? {}).some((configuration) => {
        try {
            return ConnectionStringParser.parse(configuration.uri).type === "p2p";
        } catch {
            return false;
        }
    });
}

/**
 * Obsidian-specific P2P views, commands, status collection, and ribbon wiring.
 * Replicator ownership and lifecycle remain in Commonlib's
 * `useP2PReplicatorFeature`; this feature only consumes its current result.
 */

export function useP2PReplicatorUI(
    host: NecessaryServices<
        | "API"
        | "appLifecycle"
        | "setting"
        | "vault"
        | "database"
        | "databaseEvents"
        | "keyValueDB"
        | "replication"
        | "config"
        | "UI"
        | "replicator",
        never
    >,
    core: LiveSyncCore,
    replicator: UseP2PReplicatorResult,
    openInteractiveReplication: OpenInteractiveP2PReplication
) {
    const api = host.services.API as {
        showWindow: (type: string) => Promise<void>;
        showWindowOnRight?: (type: string) => Promise<void>;
        registerWindow: (type: string, factory: (leaf: WorkspaceLeaf) => unknown) => void;
        addCommand: (command: {
            id: string;
            name: string;
            callback?: () => void;
            checkCallback?: (checking: boolean) => boolean | void;
        }) => unknown;
        addRibbonIcon: (
            icon: string,
            title: string,
            callback: () => void
        ) => { addClass?: (name: string) => unknown; remove?: () => void } | undefined;
    };

    const statusFactory = (leaf: WorkspaceLeaf) => {
        return new P2PServerStatusPaneView(leaf, core, replicator);
    };
    const legacyStatusFactory = (leaf: WorkspaceLeaf) => {
        return new LegacyP2PStatusPaneView(leaf, core, replicator);
    };
    const openStatusPane = () => {
        if (api.showWindowOnRight) {
            return api.showWindowOnRight(VIEW_TYPE_P2P_SERVER_STATUS);
        }
        return api.showWindow(VIEW_TYPE_P2P_SERVER_STATUS);
    };
    const runOpenReplication = () => {
        // Peer selection is a host UI concern. The injected operation opens
        // the dialogue, while its transfer callbacks use focused P2P views.
        void host.services.replicator.runFiniteReplicationActivity(() => openInteractiveReplication(true), {
            label: "replication",
        });
    };
    // Keep the retired view type registered only long enough to restore an
    // existing workspace leaf with the current status UI. Layout-ready
    // migration below rewrites it to the current type without opening a leaf.
    api.registerWindow(LEGACY_VIEW_TYPE_P2P, legacyStatusFactory);
    api.registerWindow(VIEW_TYPE_P2P_SERVER_STATUS, statusFactory);

    let ribbonElement: { addClass?: (name: string) => unknown; remove?: () => void } | undefined;
    const updateRibbon = (settings: Partial<ObsidianLiveSyncSettings>) => {
        if (hasP2PConfiguration(settings)) {
            if (ribbonElement) return;
            ribbonElement = api.addRibbonIcon("waypoints", "P2P Status", () => {
                void openStatusPane();
            });
            ribbonElement?.addClass?.("livesync-ribbon-p2p-server-status");
            return;
        }
        ribbonElement?.remove?.();
        ribbonElement = undefined;
    };

    // Settings are loaded after onInitialise. Reading them from the earlier
    // phase aborts the plug-in lifecycle before the local database can open.
    host.services.appLifecycle.onSettingLoaded.addHandler(() => {
        updateRibbon(host.services.setting.currentSettings());
        return Promise.resolve(true);
    });

    host.services.appLifecycle.onInitialise.addHandler(() => {
        eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => {
            void openStatusPane();
        });

        api.addCommand({
            id: "open-p2p-server-status",
            name: "P2P Sync : Open P2P Status",
            checkCallback: (checking) => {
                if (!hasP2PConfiguration(host.services.setting.currentSettings())) return false;
                if (!checking) {
                    void openStatusPane();
                }
                return true;
            },
        });
        host.services.API.addCommand({
            id: "replicate-now-by-p2p-default-peer",
            name: "Replicate P2P to default peer",
            checkCallback: (isChecking: boolean) => {
                const settings = host.services.setting.currentSettings();
                const isAvailable =
                    hasP2PConfiguration(settings) &&
                    settings.remoteType !== REMOTE_P2P &&
                    replicator.transportLifecycle.isConnected;
                if (!isAvailable) return false;
                if (!isChecking) {
                    runOpenReplication();
                }
                return true;
            },
        });
        host.services.API.addCommand({
            id: "replicate-now-by-p2p",
            name: "Replicate now by P2P",
            checkCallback: (isChecking: boolean) => {
                const settings = host.services.setting.currentSettings();
                const isAvailable =
                    hasP2PConfiguration(settings) &&
                    settings.remoteType !== REMOTE_P2P &&
                    replicator.transportLifecycle.isConnected;
                if (!isAvailable) return false;
                if (!isChecking) {
                    runOpenReplication();
                }
                return true;
            },
        });

        host.services.API.addCommand({
            id: "p2p-sync-targets",
            name: "P2P: Sync with targets",
            checkCallback: (isChecking: boolean) => {
                const isAvailable =
                    hasP2PConfiguration(host.services.setting.currentSettings()) &&
                    replicator.transportLifecycle.isConnected;
                if (!isAvailable) return false;
                if (!isChecking) {
                    void host.services.replicator.runFiniteReplicationActivity(
                        () => replicator.targetedTransfer.synchroniseConfiguredTargets(),
                        { label: "replication" }
                    );
                }
                return true;
            },
        });

        host.services.setting.onSettingSaved?.addHandler((settings) => {
            updateRibbon(settings);
            return Promise.resolve(true);
        });

        return Promise.resolve(true);
    });

    host.services.appLifecycle.onLayoutReady.addHandler(async () => {
        const workspace = (
            host.services.context as {
                app?: {
                    workspace?: {
                        getLeavesOfType(type: string): WorkspaceLeaf[];
                    };
                };
            }
        ).app?.workspace;
        if (!workspace) {
            return true;
        }
        const legacyLeaves = workspace.getLeavesOfType(LEGACY_VIEW_TYPE_P2P);
        await Promise.all(
            legacyLeaves.map((leaf) =>
                leaf.setViewState({
                    type: VIEW_TYPE_P2P_SERVER_STATUS,
                    active: false,
                })
            )
        );
        return true;
    });
}
