"use client";
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useCallback, useEffect, useRef, useMemo, } from "react";
import { useGitHubRepos } from "./useGitHubRepos";
import { useGitHubSearch } from "./useGitHubSearch";
const RECENT_REPOS_KEY = "stigmer:github:recent-repos";
const MAX_RECENT = 3;
function getRecentRepos() {
    try {
        const stored = localStorage.getItem(RECENT_REPOS_KEY);
        return stored ? JSON.parse(stored) : [];
    }
    catch {
        return [];
    }
}
function addRecentRepo(repo) {
    const current = getRecentRepos().filter((r) => !(r.owner === repo.owner && r.name === repo.name));
    const updated = [repo, ...current].slice(0, MAX_RECENT);
    localStorage.setItem(RECENT_REPOS_KEY, JSON.stringify(updated));
}
function groupRepos(filteredRepos, recentEntries) {
    const groups = [];
    const repoLookup = new Map(filteredRepos.map((r) => [`${r.owner}/${r.name}`, r]));
    const recentMatched = recentEntries
        .map((r) => repoLookup.get(`${r.owner}/${r.name}`))
        .filter((r) => r !== undefined);
    if (recentMatched.length > 0) {
        groups.push({
            key: "recent",
            label: "Recent",
            isRecent: true,
            repos: recentMatched,
        });
    }
    const ownerMap = new Map();
    for (const repo of filteredRepos) {
        const existing = ownerMap.get(repo.owner);
        if (existing) {
            existing.repos.push(repo);
        }
        else {
            ownerMap.set(repo.owner, { ownerType: repo.ownerType, repos: [repo] });
        }
    }
    const sortedOwners = [...ownerMap.entries()].sort(([, a], [, b]) => {
        if (a.ownerType !== b.ownerType)
            return a.ownerType === "User" ? -1 : 1;
        return b.repos.length - a.repos.length;
    });
    for (const [owner, { repos }] of sortedOwners) {
        groups.push({ key: owner, label: owner, isRecent: false, repos });
    }
    return groups;
}
// ---------------------------------------------------------------------------
// Search highlighting
// ---------------------------------------------------------------------------
function HighlightMatch({ text, query }) {
    if (!query)
        return _jsx(_Fragment, { children: text });
    const idx = text.toLowerCase().indexOf(query.toLowerCase());
    if (idx === -1)
        return _jsx(_Fragment, { children: text });
    return (_jsxs(_Fragment, { children: [text.slice(0, idx), _jsx("span", { className: "text-foreground font-medium", children: text.slice(idx, idx + query.length) }), text.slice(idx + query.length)] }));
}
// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------
const LIST_ID = "stgm-repo-list";
const GITHUB_INSTALLATIONS_URL = "https://github.com/settings/installations";
/**
 * Styled component for browsing and selecting a GitHub repository.
 *
 * Features:
 * - Two modes: "My Repos" (user's own repos) and "All GitHub" (public search)
 * - Owner-grouped sections in My Repos mode
 * - Recently selected repos pinned at top
 * - Fixed max-height with scroll shadow indicators
 * - Keyboard navigation (Arrow keys, Enter, Escape)
 * - Search with match highlighting
 * - Branch selector after repo selection
 * - Manual URL entry for repos not discoverable via search
 * - Link to manage GitHub App repository access
 *
 * All visual properties flow through `--stgm-*` tokens.
 *
 * @example
 * ```tsx
 * function RepoSelector({ token }: { token: string }) {
 *   return (
 *     <GitHubRepoPicker
 *       token={token}
 *       onSelect={(url, branch) => workspace.addGitRepo(url, branch)}
 *       onCancel={() => setShowPicker(false)}
 *     />
 *   );
 * }
 * ```
 */
export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
    const [mode, setMode] = useState("my-repos");
    const [showManualEntry, setShowManualEntry] = useState(false);
    // My Repos data
    const myRepos = useGitHubRepos(token);
    // All GitHub search data
    const githubSearch = useGitHubSearch(token);
    // Branch selection state (shared across modes)
    const [selectedRepo, setSelectedRepo] = useState(null);
    const [branches, setBranches] = useState([]);
    const [selectedBranch, setSelectedBranch] = useState("");
    const [loadingBranches, setLoadingBranches] = useState(false);
    // Manual URL state
    const [manualUrl, setManualUrl] = useState("");
    const [manualBranch, setManualBranch] = useState("");
    // Keyboard navigation
    const [focusIndex, setFocusIndex] = useState(-1);
    const listRef = useRef(null);
    const searchRef = useRef(null);
    // Scroll shadow state
    const [canScrollUp, setCanScrollUp] = useState(false);
    const [canScrollDown, setCanScrollDown] = useState(false);
    // Recent repos
    const [recentRepos, setRecentRepos] = useState(getRecentRepos);
    // --- Mode-dependent derived state ---
    const activeSearch = mode === "my-repos" ? myRepos.search : githubSearch.query;
    const setActiveSearch = mode === "my-repos" ? myRepos.setSearch : githubSearch.setQuery;
    const activeRepos = mode === "my-repos" ? myRepos.repos : githubSearch.results;
    const activeError = mode === "my-repos" ? myRepos.error : githubSearch.error;
    const activeIsLoading = mode === "my-repos" ? myRepos.isLoading : githubSearch.isSearching;
    // Group repos only in "my-repos" mode
    const groups = useMemo(() => (mode === "my-repos" ? groupRepos(activeRepos, recentRepos) : []), [mode, activeRepos, recentRepos]);
    // Flat list for keyboard nav: grouped in my-repos, flat in all-github
    const flatItems = useMemo(() => {
        if (mode === "all-github")
            return [...activeRepos];
        const items = [];
        for (const group of groups) {
            for (const repo of group.repos) {
                items.push(repo);
            }
        }
        return items;
    }, [mode, activeRepos, groups]);
    // Reset focus index when search or mode changes
    useEffect(() => {
        setFocusIndex(-1);
    }, [activeSearch, mode]);
    // Scroll focused item into view
    useEffect(() => {
        if (focusIndex >= 0) {
            const el = listRef.current?.querySelector(`[data-idx="${focusIndex}"]`);
            el?.scrollIntoView({ block: "nearest" });
        }
    }, [focusIndex]);
    // Scroll shadow tracking
    const updateScrollShadows = useCallback(() => {
        const el = listRef.current;
        if (!el)
            return;
        setCanScrollUp(el.scrollTop > 0);
        setCanScrollDown(el.scrollTop + el.clientHeight < el.scrollHeight - 1);
    }, []);
    useEffect(() => {
        const el = listRef.current;
        if (!el)
            return;
        el.addEventListener("scroll", updateScrollShadows, { passive: true });
        updateScrollShadows();
        return () => el.removeEventListener("scroll", updateScrollShadows);
    }, [updateScrollShadows]);
    useEffect(() => {
        updateScrollShadows();
    }, [activeRepos, updateScrollShadows]);
    const handleModeSwitch = useCallback((newMode) => {
        if (newMode === mode)
            return;
        setMode(newMode);
        setFocusIndex(-1);
        if (newMode === "my-repos") {
            githubSearch.setQuery("");
        }
        else {
            myRepos.setSearch("");
        }
        setTimeout(() => searchRef.current?.focus(), 0);
    }, [mode, githubSearch, myRepos]);
    const handleRepoClick = useCallback(async (repo) => {
        setSelectedRepo({
            owner: repo.owner,
            name: repo.name,
            cloneUrl: repo.cloneUrl,
            defaultBranch: repo.defaultBranch,
        });
        setSelectedBranch(repo.defaultBranch);
        setLoadingBranches(true);
        const b = await myRepos.fetchBranches(repo.owner, repo.name);
        setBranches(b);
        setLoadingBranches(false);
    }, [myRepos]);
    const handleAdd = useCallback(() => {
        if (selectedRepo && selectedBranch) {
            addRecentRepo({
                owner: selectedRepo.owner,
                name: selectedRepo.name,
                cloneUrl: selectedRepo.cloneUrl,
                defaultBranch: selectedRepo.defaultBranch,
            });
            setRecentRepos(getRecentRepos());
            onSelect(selectedRepo.cloneUrl, selectedBranch);
            setSelectedRepo(null);
            setBranches([]);
            setSelectedBranch("");
        }
    }, [selectedRepo, selectedBranch, onSelect]);
    const handleManualAdd = useCallback(() => {
        const url = manualUrl.trim();
        if (!url)
            return;
        onSelect(url, manualBranch.trim() || "main");
        setManualUrl("");
        setManualBranch("");
        setShowManualEntry(false);
    }, [manualUrl, manualBranch, onSelect]);
    const handleSearchKeyDown = useCallback((e) => {
        if (e.key === "ArrowDown") {
            e.preventDefault();
            setFocusIndex((prev) => prev < flatItems.length - 1 ? prev + 1 : prev);
        }
        else if (e.key === "ArrowUp") {
            e.preventDefault();
            setFocusIndex((prev) => (prev > 0 ? prev - 1 : -1));
        }
        else if (e.key === "Enter" &&
            focusIndex >= 0 &&
            focusIndex < flatItems.length) {
            e.preventDefault();
            handleRepoClick(flatItems[focusIndex]);
        }
        else if (e.key === "Enter") {
            e.preventDefault();
        }
        else if (e.key === "Escape") {
            e.preventDefault();
            if (activeSearch) {
                setActiveSearch("");
                setFocusIndex(-1);
            }
            else {
                onCancel?.();
            }
        }
    }, [flatItems, focusIndex, handleRepoClick, onCancel, activeSearch, setActiveSearch]);
    const handleManualKeyDown = useCallback((e) => {
        if (e.key === "Enter") {
            e.preventDefault();
            handleManualAdd();
        }
        else if (e.key === "Escape") {
            e.preventDefault();
            setShowManualEntry(false);
            setTimeout(() => searchRef.current?.focus(), 0);
        }
    }, [handleManualAdd]);
    // --- Branch selection view ---
    if (selectedRepo) {
        return (_jsxs("div", { className: ["space-y-2", className].filter(Boolean).join(" "), children: [_jsxs("div", { className: "flex items-center gap-2 text-xs text-foreground", children: [_jsx("button", { type: "button", onClick: () => {
                                setSelectedRepo(null);
                                setBranches([]);
                                setTimeout(() => searchRef.current?.focus(), 0);
                            }, className: "text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Back to repo list", children: _jsx(ChevronLeftIcon, {}) }), _jsxs("span", { className: "font-medium truncate", children: [selectedRepo.owner, "/", selectedRepo.name] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-[0.65rem] text-muted-foreground", children: "Branch" }), loadingBranches ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Loading branches..." })) : (_jsx("select", { value: selectedBranch, onChange: (e) => setSelectedBranch(e.target.value), className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: branches.map((b) => (_jsx("option", { value: b.name, children: b.name }, b.name))) }))] }), _jsx("div", { className: "flex justify-end", children: _jsx("button", { type: "button", onClick: handleAdd, disabled: !selectedBranch, className: "rounded-md bg-primary px-2.5 py-1 text-xs text-primary-foreground hover:bg-primary-hover transition-colors disabled:opacity-40", children: "Add" }) })] }));
    }
    // --- Manual URL entry view ---
    if (showManualEntry) {
        return (_jsxs("div", { className: ["space-y-2", className].filter(Boolean).join(" "), children: [_jsxs("div", { className: "flex items-center gap-2 text-xs text-foreground", children: [_jsx("button", { type: "button", onClick: () => {
                                setShowManualEntry(false);
                                setTimeout(() => searchRef.current?.focus(), 0);
                            }, className: "text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Back to repo list", children: _jsx(ChevronLeftIcon, {}) }), _jsx("span", { className: "font-medium", children: "Paste a repository URL" })] }), _jsx("input", { type: "url", placeholder: "https://github.com/org/repo", value: manualUrl, onChange: (e) => setManualUrl(e.target.value), onKeyDown: handleManualKeyDown, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", autoFocus: true }), _jsx("input", { type: "text", placeholder: "Branch (optional, defaults to main)", value: manualBranch, onChange: (e) => setManualBranch(e.target.value), onKeyDown: handleManualKeyDown, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" }), _jsx("div", { className: "flex justify-end", children: _jsx("button", { type: "button", onClick: handleManualAdd, disabled: !manualUrl.trim(), className: "rounded-md bg-primary px-2.5 py-1 text-xs text-primary-foreground hover:bg-primary-hover transition-colors disabled:opacity-40", children: "Add" }) })] }));
    }
    // --- Compute flat index offsets per group for "my-repos" rendering ---
    let runningIndex = 0;
    const groupOffsets = groups.map((g) => {
        const offset = runningIndex;
        runningIndex += g.repos.length;
        return offset;
    });
    // --- Main repo list view ---
    return (_jsxs("div", { className: ["space-y-1.5", className].filter(Boolean).join(" "), children: [_jsxs("div", { className: "flex rounded-md border border-border bg-muted-faint p-0.5", children: [_jsx("button", { type: "button", onClick: () => handleModeSwitch("my-repos"), className: [
                            "flex-1 rounded px-2 py-1 text-[0.65rem] font-medium transition-colors",
                            mode === "my-repos"
                                ? "bg-background text-foreground shadow-sm"
                                : "text-muted-foreground hover:text-foreground",
                        ].join(" "), children: "My Repos" }), _jsx("button", { type: "button", onClick: () => handleModeSwitch("all-github"), className: [
                            "flex-1 rounded px-2 py-1 text-[0.65rem] font-medium transition-colors",
                            mode === "all-github"
                                ? "bg-background text-foreground shadow-sm"
                                : "text-muted-foreground hover:text-foreground",
                        ].join(" "), children: "All GitHub" })] }), _jsx("input", { ref: searchRef, type: "text", role: "combobox", "aria-expanded": true, "aria-controls": LIST_ID, "aria-activedescendant": focusIndex >= 0 ? `stgm-repo-${focusIndex}` : undefined, placeholder: mode === "my-repos"
                    ? "Search repositories..."
                    : "Search all of GitHub...", value: activeSearch, onChange: (e) => setActiveSearch(e.target.value), onKeyDown: handleSearchKeyDown, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", autoFocus: true }), activeError && (_jsx("p", { className: "text-xs text-destructive", children: activeError })), _jsxs("div", { className: "relative", children: [canScrollUp && (_jsx("div", { className: "absolute inset-x-0 top-0 h-3 z-10 pointer-events-none", style: {
                            background: "linear-gradient(to bottom, var(--color-card, hsl(0 0% 9%)), transparent)",
                        } })), _jsx("div", { ref: listRef, id: LIST_ID, role: "listbox", "aria-label": "Repositories", className: "max-h-64 overflow-y-auto", children: mode === "my-repos" ? (_jsx(MyReposList, { groups: groups, groupOffsets: groupOffsets, flatItems: flatItems, focusIndex: focusIndex, isLoading: myRepos.isLoading, isBackgroundLoading: myRepos.isBackgroundLoading, search: myRepos.search, onRepoClick: handleRepoClick })) : (_jsx(SearchResultsList, { results: githubSearch.results, focusIndex: focusIndex, isSearching: githubSearch.isSearching, query: githubSearch.query, totalCount: githubSearch.totalCount, hasMore: githubSearch.hasMore, onRepoClick: handleRepoClick, onLoadMore: githubSearch.loadMore })) }), canScrollDown && (_jsx("div", { className: "absolute inset-x-0 bottom-0 h-3 z-10 pointer-events-none", style: {
                            background: "linear-gradient(to top, var(--color-card, hsl(0 0% 9%)), transparent)",
                        } }))] }), _jsxs("div", { className: "flex items-center gap-3 border-t border-border pt-1.5 text-[0.65rem] text-muted-foreground", children: [_jsx("button", { type: "button", onClick: () => setShowManualEntry(true), className: "hover:text-foreground transition-colors", children: "Paste a URL" }), _jsx("span", { className: "opacity-30", children: "\u00B7" }), _jsx("a", { href: GITHUB_INSTALLATIONS_URL, target: "_blank", rel: "noopener noreferrer", className: "hover:text-foreground transition-colors", children: "Manage access" })] })] }));
}
// ---------------------------------------------------------------------------
// My Repos list (grouped)
// ---------------------------------------------------------------------------
function MyReposList({ groups, groupOffsets, flatItems, focusIndex, isLoading, isBackgroundLoading, search, onRepoClick, }) {
    if (isLoading)
        return _jsx(LoadingSkeleton, {});
    if (flatItems.length === 0) {
        return (_jsx("div", { className: "py-4 text-center text-xs text-muted-foreground", children: search ? "No repos match your search" : "No repositories found" }));
    }
    return (_jsxs(_Fragment, { children: [groups.map((group, gi) => (_jsxs("div", { children: [_jsxs("div", { className: "sticky top-0 z-[1] bg-card px-2 py-1 text-[0.65rem] font-medium text-muted-foreground backdrop-blur-sm", children: [group.label, !group.isRecent && (_jsxs("span", { className: "ml-1 opacity-50", children: ["(", group.repos.length, ")"] }))] }), group.repos.map((repo, ri) => {
                        const flatIdx = groupOffsets[gi] + ri;
                        return (_jsx(RepoRow, { repo: repo, flatIdx: flatIdx, focusIndex: focusIndex, displayName: group.isRecent ? repo.fullName : repo.name, query: search, onClick: onRepoClick }, `${group.key}-${repo.id}`));
                    })] }, group.key))), isBackgroundLoading && (_jsx("div", { className: "py-1 text-center text-[0.6rem] text-muted-foreground", children: "Loading more..." }))] }));
}
// ---------------------------------------------------------------------------
// All GitHub search results list (flat)
// ---------------------------------------------------------------------------
function SearchResultsList({ results, focusIndex, isSearching, query, totalCount, hasMore, onRepoClick, onLoadMore, }) {
    if (!query) {
        return (_jsx("div", { className: "py-6 text-center text-xs text-muted-foreground", children: "Type to search all of GitHub" }));
    }
    if (isSearching && results.length === 0) {
        return _jsx(LoadingSkeleton, {});
    }
    if (results.length === 0) {
        return (_jsx("div", { className: "py-4 text-center text-xs text-muted-foreground", children: "No repositories found" }));
    }
    return (_jsxs(_Fragment, { children: [totalCount > 0 && (_jsxs("div", { className: "px-2 py-1 text-[0.6rem] text-muted-foreground", children: [totalCount.toLocaleString(), " ", totalCount === 1 ? "result" : "results"] })), results.map((repo, i) => (_jsx(RepoRow, { repo: repo, flatIdx: i, focusIndex: focusIndex, displayName: repo.fullName, query: query, onClick: onRepoClick }, repo.id))), isSearching && (_jsx("div", { className: "py-1 text-center text-[0.6rem] text-muted-foreground", children: "Searching..." })), hasMore && !isSearching && (_jsx("button", { type: "button", onClick: onLoadMore, className: "w-full py-1.5 text-center text-[0.65rem] text-muted-foreground hover:text-foreground transition-colors", children: "Load more results" }))] }));
}
// ---------------------------------------------------------------------------
// Shared repo row
// ---------------------------------------------------------------------------
function RepoRow({ repo, flatIdx, focusIndex, displayName, query, onClick, }) {
    return (_jsxs("button", { id: `stgm-repo-${flatIdx}`, type: "button", "data-idx": flatIdx, onClick: () => onClick(repo), className: [
            "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs transition-colors",
            flatIdx === focusIndex
                ? "bg-accent text-foreground"
                : "text-foreground hover:bg-accent-hover",
        ].join(" "), role: "option", "aria-selected": flatIdx === focusIndex, children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: _jsx(HighlightMatch, { text: displayName, query: query }) }), _jsx("span", { className: "shrink-0 rounded px-1 py-0.5 text-[0.6rem] bg-muted text-muted-foreground", children: repo.isPrivate ? "private" : "public" })] }));
}
// ---------------------------------------------------------------------------
// Loading skeleton
// ---------------------------------------------------------------------------
function LoadingSkeleton() {
    return (_jsxs("div", { className: "space-y-1 py-1", children: [_jsx("div", { className: "px-2 py-1", children: _jsx("div", { className: "h-2.5 w-16 rounded bg-muted animate-pulse" }) }), [55, 70, 40, 62].map((w, i) => (_jsx("div", { className: "flex items-center gap-2 px-2 py-1.5", children: _jsx("div", { className: "h-3 rounded bg-muted animate-pulse", style: { width: `${w}%` } }) }, i))), _jsx("div", { className: "px-2 py-1", children: _jsx("div", { className: "h-2.5 w-20 rounded bg-muted animate-pulse" }) }), [48, 65, 53].map((w, i) => (_jsx("div", { className: "flex items-center gap-2 px-2 py-1.5", children: _jsx("div", { className: "h-3 rounded bg-muted animate-pulse", style: { width: `${w}%` } }) }, i + 4)))] }));
}
// ---------------------------------------------------------------------------
// Icons
// ---------------------------------------------------------------------------
function ChevronLeftIcon() {
    return (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M9 3L5 7L9 11" }) }));
}
//# sourceMappingURL=GitHubRepoPicker.js.map