// Hook owning the multi-pane agent layout. Replaces the single
// `activeAgentId` state and `setActiveAgentIdWithStorage` plumbing in
// Agents.tsx with a layout of N panes plus a focused pane id.
//
// Persistence: layout JSON in localStorageService under
// `editor.agentPanesLayout`. On first run, migrates from legacy
// `editor.activeAgentId` (a single agent id) into a single-pane layout.
//
// Splitter sizes are NOT persisted here — Splitter.tsx owns its own
// localStorage. This hook only owns which panes exist and which is focused.
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { localStorageService } from "../services/localStorageService";
import { AGENT_PANES_LAYOUT_STORAGE_KEY, LEGACY_ACTIVE_AGENT_STORAGE_KEY, } from "./agentPanesTypes";
let paneCounter = 0;
function newPaneId() {
    paneCounter += 1;
    return `pane-${Date.now().toString(36)}-${paneCounter}`;
}
function makeOverviewLayout() {
    const id = newPaneId();
    return {
        orientation: "horizontal",
        rows: [[{ id, view: { kind: "overview" } }]],
        focusedPaneId: id,
    };
}
function makeAgentLayout(agentId) {
    const id = newPaneId();
    return {
        orientation: "horizontal",
        rows: [[{ id, view: { kind: "agent", agentId } }]],
        focusedPaneId: id,
    };
}
function makeProfileSelectorLayout() {
    const id = newPaneId();
    return {
        orientation: "horizontal",
        rows: [[{ id, view: { kind: "profile-selector" } }]],
        focusedPaneId: id,
    };
}
/**
 * Validate a layout that was just loaded from localStorage. Returns null if
 * it's structurally invalid, or a sanitized copy if it's mostly fine.
 */
function sanitizeStoredLayout(raw) {
    if (!raw || typeof raw !== "object")
        return null;
    const candidate = raw;
    if (candidate.orientation !== "horizontal" &&
        candidate.orientation !== "vertical" &&
        candidate.orientation !== "grid") {
        return null;
    }
    if (!Array.isArray(candidate.rows) || candidate.rows.length === 0) {
        return null;
    }
    const cleanedRows = [];
    for (const row of candidate.rows) {
        if (!Array.isArray(row) || row.length === 0)
            continue;
        const cleanedRow = [];
        for (const pane of row) {
            if (!pane || typeof pane !== "object")
                continue;
            const p = pane;
            if (typeof p.id !== "string" || !p.id)
                continue;
            const view = p.view;
            if (!view || typeof view !== "object")
                continue;
            if (view.kind !== "agent" &&
                view.kind !== "profile-selector" &&
                view.kind !== "overview") {
                continue;
            }
            if (view.kind === "agent" && typeof view.agentId !== "string")
                continue;
            cleanedRow.push({ id: p.id, view });
        }
        if (cleanedRow.length > 0)
            cleanedRows.push(cleanedRow);
    }
    if (cleanedRows.length === 0)
        return null;
    const allIds = new Set();
    for (const row of cleanedRows)
        for (const pane of row)
            allIds.add(pane.id);
    const firstPane = cleanedRows[0]?.[0];
    if (!firstPane)
        return null;
    const focusedPaneId = typeof candidate.focusedPaneId === "string" &&
        allIds.has(candidate.focusedPaneId)
        ? candidate.focusedPaneId
        : firstPane.id;
    return {
        orientation: candidate.orientation,
        rows: cleanedRows,
        focusedPaneId,
    };
}
function loadInitialLayout(options) {
    const stored = localStorageService.getItem(options.storageKey);
    const sanitized = sanitizeStoredLayout(stored);
    if (sanitized)
        return sanitized;
    if (options.migrateLegacy) {
        // Migrate from the legacy single-active-id key. Possible values:
        //   - missing: show overview
        //   - "" (sentinel): profile selector
        //   - <agent id>: single-pane agent
        const legacy = localStorageService.getString(LEGACY_ACTIVE_AGENT_STORAGE_KEY);
        if (legacy === null || legacy === undefined) {
            return makeOverviewLayout();
        }
        if (legacy === "") {
            return makeProfileSelectorLayout();
        }
        return makeAgentLayout(legacy);
    }
    return makeOverviewLayout();
}
function findPane(layout, paneId) {
    for (const [r, row] of layout.rows.entries()) {
        for (const [c, pane] of row.entries()) {
            if (pane.id === paneId)
                return { rowIndex: r, colIndex: c, pane };
        }
    }
    return null;
}
function flattenPanes(layout) {
    const out = [];
    for (const row of layout.rows)
        for (const pane of row)
            out.push(pane);
    return out;
}
function paneCount(layout) {
    return flattenPanes(layout).length;
}
/**
 * After mutating rows, ensure focus still points to a real pane. If not,
 * fall back to the first pane in row-major order.
 */
function fixFocus(rows, previous) {
    for (const row of rows)
        for (const pane of row)
            if (pane.id === previous)
                return previous;
    return rows[0]?.[0]?.id ?? previous;
}
/**
 * Collapse to a sensible empty state: a single overview pane.
 */
function ensureNonEmpty(rows) {
    if (rows.length === 0) {
        return [[{ id: newPaneId(), view: { kind: "overview" } }]];
    }
    return rows;
}
export function useAgentPanes(_agents, options = {}) {
    const storageKey = options.storageKey ?? AGENT_PANES_LAYOUT_STORAGE_KEY;
    const migrateLegacy = options.migrateLegacy ?? false;
    const [layout, setLayout] = useState(() => loadInitialLayout({ storageKey, migrateLegacy }));
    const layoutRef = useRef(layout);
    layoutRef.current = layout;
    // Persist the layout. Splitter sizes round-trip through Splitter's own
    // localStorage; this only persists pane structure + focus.
    useEffect(() => {
        localStorageService.setItem(storageKey, layout);
        // Drop the legacy single-active-id key on first save of the new layout
        // (only applicable for the primary tabbed view).
        if (migrateLegacy) {
            localStorageService.removeItem(LEGACY_ACTIVE_AGENT_STORAGE_KEY);
        }
    }, [layout, storageKey, migrateLegacy]);
    const panesByAgentId = useMemo(() => {
        const map = new Map();
        for (const row of layout.rows) {
            for (const pane of row) {
                if (pane.view.kind === "agent") {
                    const existing = map.get(pane.view.agentId);
                    if (existing)
                        existing.push(pane.id);
                    else
                        map.set(pane.view.agentId, [pane.id]);
                }
            }
        }
        return map;
    }, [layout]);
    const visibleAgentIds = useMemo(() => new Set(panesByAgentId.keys()), [panesByAgentId]);
    const focusedAgentId = useMemo(() => {
        const found = findPane(layout, layout.focusedPaneId);
        if (found && found.pane.view.kind === "agent") {
            return found.pane.view.agentId;
        }
        return null;
    }, [layout]);
    const setFocusedPane = useCallback((paneId) => {
        setLayout((prev) => {
            if (prev.focusedPaneId === paneId)
                return prev;
            // Confirm the pane exists before switching.
            if (!findPane(prev, paneId))
                return prev;
            return { ...prev, focusedPaneId: paneId };
        });
    }, []);
    const replaceFocusedPaneView = useCallback((view) => {
        setLayout((prev) => {
            const found = findPane(prev, prev.focusedPaneId);
            if (!found)
                return prev;
            const newRows = prev.rows.map((row) => row.map((pane) => pane.id === prev.focusedPaneId ? { ...pane, view } : pane));
            return { ...prev, rows: newRows };
        });
    }, []);
    const showAgentInFocusedPane = useCallback((agentId) => {
        replaceFocusedPaneView({ kind: "agent", agentId });
    }, [replaceFocusedPaneView]);
    const showProfileSelectorInFocusedPane = useCallback(() => {
        replaceFocusedPaneView({ kind: "profile-selector" });
    }, [replaceFocusedPaneView]);
    const showOverviewInFocusedPane = useCallback(() => {
        replaceFocusedPaneView({ kind: "overview" });
    }, [replaceFocusedPaneView]);
    const splitPane = useCallback((sourcePaneId, agentId, direction) => {
        setLayout((prev) => {
            const found = findPane(prev, sourcePaneId);
            if (!found)
                return prev;
            const newPane = {
                id: newPaneId(),
                view: agentId === null
                    ? { kind: "profile-selector" }
                    : { kind: "agent", agentId },
            };
            if (direction === "right") {
                const rows = prev.rows.map((row, r) => r === found.rowIndex
                    ? [
                        ...row.slice(0, found.colIndex + 1),
                        newPane,
                        ...row.slice(found.colIndex + 1),
                    ]
                    : row);
                // Single-row + horizontal split = "horizontal" orientation. With
                // multiple rows we must be in grid mode.
                const orientation = rows.length > 1 ? "grid" : "horizontal";
                return {
                    ...prev,
                    rows,
                    orientation,
                    focusedPaneId: newPane.id,
                };
            }
            // direction === "down": insert a new row below the source's row,
            // containing only the new pane. This becomes a 2-D grid.
            const rows = [
                ...prev.rows.slice(0, found.rowIndex + 1),
                [newPane],
                ...prev.rows.slice(found.rowIndex + 1),
            ];
            const orientation = rows.length > 1
                ? rows.some((r) => r.length > 1)
                    ? "grid"
                    : "vertical"
                : "horizontal";
            return {
                ...prev,
                rows,
                orientation,
                focusedPaneId: newPane.id,
            };
        });
    }, []);
    const closePane = useCallback((paneId) => {
        setLayout((prev) => {
            if (paneCount(prev) <= 1) {
                // Last pane: never remove it; reset its view to overview instead.
                const rows = prev.rows.map((row) => row.map((pane) => pane.id === paneId
                    ? { ...pane, view: { kind: "overview" } }
                    : pane));
                return { ...prev, rows, focusedPaneId: paneId };
            }
            const filteredRows = prev.rows
                .map((row) => row.filter((pane) => pane.id !== paneId))
                .filter((row) => row.length > 0);
            const rows = ensureNonEmpty(filteredRows);
            const orientation = rows.length > 1
                ? rows.some((r) => r.length > 1)
                    ? "grid"
                    : "vertical"
                : "horizontal";
            return {
                ...prev,
                rows,
                orientation,
                focusedPaneId: fixFocus(rows, prev.focusedPaneId),
            };
        });
    }, []);
    const closeAgentEverywhere = useCallback((agentId) => {
        setLayout((prev) => {
            let changed = false;
            const rows = prev.rows.map((row) => row.map((pane) => {
                if (pane.view.kind === "agent" && pane.view.agentId === agentId) {
                    changed = true;
                    return {
                        ...pane,
                        view: { kind: "profile-selector" },
                    };
                }
                return pane;
            }));
            return changed ? { ...prev, rows } : prev;
        });
    }, []);
    const pruneMissingAgents = useCallback((existingAgentIds) => {
        const existingSet = new Set(existingAgentIds);
        setLayout((prev) => {
            let changed = false;
            const rows = prev.rows.map((row) => row.map((pane) => {
                if (pane.view.kind === "agent" &&
                    !existingSet.has(pane.view.agentId)) {
                    changed = true;
                    return { ...pane, view: { kind: "profile-selector" } };
                }
                return pane;
            }));
            if (!changed)
                return prev;
            return {
                ...prev,
                rows,
                focusedPaneId: fixFocus(rows, prev.focusedPaneId),
            };
        });
    }, []);
    // Test-only escape hatch. Note: paneCounter resets between page loads,
    // not between tests; tests should rely on stable-shaped IDs rather than
    // exact matching.
    void layoutRef;
    return {
        layout,
        focusedPaneId: layout.focusedPaneId,
        panesByAgentId,
        visibleAgentIds,
        focusedAgentId,
        setFocusedPane,
        showAgentInFocusedPane,
        showProfileSelectorInFocusedPane,
        showOverviewInFocusedPane,
        splitPane,
        closePane,
        closeAgentEverywhere,
        pruneMissingAgents,
    };
}
//# sourceMappingURL=useAgentPanes.js.map