import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useMemo, useCallback } from "react";
import { Button } from "../../../components/ui/button";
import { SettingsPanelLayout } from "../SettingsPanelLayout";
import { SettingsCard as Card } from "../SettingsCard";
import { Input } from "../../../components/ui/input";
import { Select } from "../../../components/ui/select";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../../../components/ui/dialog";
import { setupWizardService, getImportableProviders, } from "../../../setup/services/setupWizardService";
import { AlertCircle, RefreshCw, Upload, Plus, ArrowRight, Brain, Image, Cloud, Copy, Sparkles, Layers, } from "lucide-react";
import { DeleteIcon } from "../../../components/ui/delete-icon";
import { ProviderIcon } from "./ProviderIcon";
import { useSearchParams } from "../../client/navigation";
import { AddModelDialog } from "../../../setup/wizard/steps/AddModelDialog";
import { ImportModelDialog } from "../../../setup/wizard/steps/ImportModelDialog";
import { Splitter } from "../../ui/Splitter";
import { ModelConfigPanel } from "./ModelConfigPanel";
import { useEditContext } from "../../client/editContext";
import { SearchInput } from "../../../components/SearchInput";
import { useRequiredSettingsHeaderActions } from "../SettingsHeaderActionsContext";
const SELECTED_MODEL_QUERY_PARAM = "modelId";
/**
 * True when a numeric price is present (including 0). Used for display and
 * "missing pricing" detection.
 */
const hasDefinedPricingValue = (value) => value !== undefined && value !== null;
const hasUsableImagePricing = (model) => hasDefinedPricingValue(model.pricing?.inputTextTokenCostPerMillion) ||
    hasDefinedPricingValue(model.pricing?.inputImageTokenCostPerMillion) ||
    hasDefinedPricingValue(model.pricing?.outputImageTokenCostPerMillion);
/**
 * True when the model row has nothing to show on the pricing line (no usable
 * token/image figures). This is the same condition used to decide whether to
 * show the warning — avoids blank rows when field-level "missing" logic drifts.
 */
const isPricingLineEmpty = (model) => !hasVisiblePricingSummary(model);
const getPricingWarningMessage = (model) => {
    const template = model.templateType || "Ai Chat Model";
    if (template === "Ai Image Model") {
        return "Pricing information is missing. Configure at least one image pricing field.";
    }
    if (template === "Ai Embeddings Model") {
        return "Pricing information is missing. Set Input Cost Per Million Tokens.";
    }
    const missingFields = [];
    if (!hasDefinedPricingValue(model.pricing?.inputCostPerMillionTokens)) {
        missingFields.push("Input Cost Per Million Tokens");
    }
    if (!hasDefinedPricingValue(model.pricing?.outputCostPerMillionTokens)) {
        missingFields.push("Output Cost Per Million Tokens");
    }
    if (missingFields.length === 0) {
        return "Pricing information is missing.";
    }
    return `Pricing information is missing. Set ${missingFields.join(" and ")}.`;
};
const hasVisiblePricingSummary = (model) => {
    if (!model.pricing) {
        return false;
    }
    if (model.templateType === "Ai Image Model") {
        return hasUsableImagePricing(model);
    }
    return (hasDefinedPricingValue(model.pricing.inputCostPerMillionTokens) ||
        hasDefinedPricingValue(model.pricing.outputCostPerMillionTokens) ||
        hasDefinedPricingValue(model.pricing.cachedInputCostPerMillionTokens));
};
/** Sitecore item name for lists and dialogs; falls back to API model id / key. */
function getModelListLabel(model) {
    return model.itemName?.trim() || model.name || model.key || "";
}
function normalizeModelId(id) {
    return (id || "").replace(/[{}]/g, "").toLowerCase();
}
function extractMissingProviderModelIds(errors) {
    const modelIds = new Set();
    errors.forEach((error) => {
        const match = error.match(/^Model '(.+)' not found in provider catalog$/);
        const modelId = match?.[1]?.trim();
        if (modelId) {
            modelIds.add(modelId);
        }
    });
    return Array.from(modelIds);
}
function getModelTypeLabel(templateType) {
    switch (templateType) {
        case "Ai Chat Model":
            return "Chat";
        case "Ai Embeddings Model":
            return "Embeddings";
        case "Ai Image Model":
            return "Image";
        default:
            return templateType;
    }
}
function buildPricingSegments(model) {
    const parts = [];
    if (!model.pricing)
        return parts;
    if (model.templateType === "Ai Image Model") {
        if (hasDefinedPricingValue(model.pricing.inputTextTokenCostPerMillion)) {
            parts.push(`In text $${model.pricing.inputTextTokenCostPerMillion.toFixed(2)}/M`);
        }
        if (hasDefinedPricingValue(model.pricing.inputImageTokenCostPerMillion)) {
            parts.push(`In img $${model.pricing.inputImageTokenCostPerMillion.toFixed(2)}/M`);
        }
        if (hasDefinedPricingValue(model.pricing.outputImageTokenCostPerMillion)) {
            parts.push(`Out img $${model.pricing.outputImageTokenCostPerMillion.toFixed(2)}/M`);
        }
    }
    else {
        if (hasDefinedPricingValue(model.pricing.inputCostPerMillionTokens)) {
            parts.push(`In $${model.pricing.inputCostPerMillionTokens.toFixed(2)}/M`);
        }
        if (hasDefinedPricingValue(model.pricing.outputCostPerMillionTokens)) {
            parts.push(`Out $${model.pricing.outputCostPerMillionTokens.toFixed(2)}/M`);
        }
        if (hasDefinedPricingValue(model.pricing.cachedInputCostPerMillionTokens)) {
            parts.push(`Cached $${model.pricing.cachedInputCostPerMillionTokens.toFixed(2)}/M`);
        }
    }
    return parts;
}
function buildContextSegments(model) {
    const parts = [];
    if (model.maxContextWindowTokens) {
        parts.push(`${model.maxContextWindowTokens.toLocaleString()} ctx`);
    }
    if (model.maxResponseTokens) {
        parts.push(`${model.maxResponseTokens.toLocaleString()} max out`);
    }
    return parts;
}
/**
 * Settings panel for AI Models configuration.
 * Install and configure AI models for chat and other tasks.
 */
export function ModelsPanel() {
    const editContext = useEditContext();
    const setSettingsHeaderActions = useRequiredSettingsHeaderActions();
    const searchParams = useSearchParams();
    const [state, setState] = useState("loading");
    const [installedModels, setInstalledModels] = useState([]);
    const [providers, setProviders] = useState([]);
    const [settingsExists, setSettingsExists] = useState(false);
    const [defaultChatModelId, setDefaultChatModelId] = useState("");
    const [defaultImageModelId, setDefaultImageModelId] = useState("");
    const [savingDefaults, setSavingDefaults] = useState(false);
    const [error, setError] = useState(null);
    const [updating, setUpdating] = useState(null);
    // Dialog states
    const [showAddDialog, setShowAddDialog] = useState(false);
    const [showImportDialog, setShowImportDialog] = useState(false);
    // URL-synced model selection
    const [urlSelectedModelId, setUrlSelectedModelId] = useState(searchParams.get(SELECTED_MODEL_QUERY_PARAM) ||
        (typeof window !== "undefined"
            ? new URLSearchParams(window.location.search).get(SELECTED_MODEL_QUERY_PARAM)
            : null));
    const [selectedModel, setSelectedModel] = useState(null);
    // Bulk pricing update state
    const [updatingAllPricing, setUpdatingAllPricing] = useState(false);
    const [unavailableModels, setUnavailableModels] = useState([]);
    const [removingUnavailableModels, setRemovingUnavailableModels] = useState(false);
    // Delete state
    const [deleting, setDeleting] = useState(null);
    // Duplicate state
    const [duplicatingId, setDuplicatingId] = useState(null);
    const [duplicateDialogModel, setDuplicateDialogModel] = useState(null);
    const [duplicateItemName, setDuplicateItemName] = useState("");
    // Provider types (discovered from backend)
    const [providerTypes, setProviderTypes] = useState([]);
    // Filter state
    const [filterText, setFilterText] = useState("");
    useEffect(() => {
        const key = searchParams.get(SELECTED_MODEL_QUERY_PARAM) ||
            (typeof window !== "undefined"
                ? new URLSearchParams(window.location.search).get(SELECTED_MODEL_QUERY_PARAM)
                : null);
        setUrlSelectedModelId(key);
    }, [searchParams]);
    useEffect(() => {
        if (typeof window === "undefined")
            return;
        const handlePopState = () => {
            setUrlSelectedModelId(new URLSearchParams(window.location.search).get(SELECTED_MODEL_QUERY_PARAM));
        };
        window.addEventListener("popstate", handlePopState);
        return () => window.removeEventListener("popstate", handlePopState);
    }, []);
    useEffect(() => {
        if (!urlSelectedModelId) {
            if (selectedModel)
                setSelectedModel(null);
            return;
        }
        const match = installedModels.find((m) => m.id === urlSelectedModelId);
        if (match && match !== selectedModel) {
            setSelectedModel(match);
        }
    }, [installedModels, urlSelectedModelId, selectedModel]);
    const updateSelectedModelUrl = useCallback((modelId) => {
        editContext?.updateUrl({
            [SELECTED_MODEL_QUERY_PARAM]: modelId || undefined,
        });
        setUrlSelectedModelId(modelId || null);
    }, [editContext]);
    const handleOpenConfig = (model) => {
        setSelectedModel(model);
        updateSelectedModelUrl(model.id);
    };
    const handleCloseConfig = () => {
        setSelectedModel(null);
        updateSelectedModelUrl(null);
    };
    const loadData = async () => {
        try {
            setState("loading");
            setError(null);
            setUnavailableModels([]);
            const [installed, providersData, providerTypesData, defaultModels, hasSettings,] = await Promise.all([
                setupWizardService.getInstalledModels(),
                setupWizardService.getProviders(),
                setupWizardService.getProviderTypes(),
                setupWizardService.getDefaultModels(),
                setupWizardService.getSettingsExists(),
            ]);
            setInstalledModels(installed);
            setProviders(providersData);
            setProviderTypes(providerTypesData);
            setSettingsExists(hasSettings);
            const resolvedChatModel = installed.find((model) => normalizeModelId(model.id) ===
                normalizeModelId(defaultModels.chatModelId));
            const resolvedImageModel = installed.find((model) => normalizeModelId(model.id) ===
                normalizeModelId(defaultModels.imageModelId));
            setDefaultChatModelId(resolvedChatModel?.id || defaultModels.chatModelId || "");
            setDefaultImageModelId(resolvedImageModel?.id || defaultModels.imageModelId || "");
            setState("success");
            return installed;
        }
        catch (e) {
            setState("error");
            setError(e?.message || "Failed to load models");
            return undefined;
        }
    };
    // Get providers that support model import (any provider with a registered provider type)
    const importableProviders = useMemo(() => {
        return getImportableProviders(providers, providerTypes);
    }, [providers, providerTypes]);
    const hasImportableProviders = importableProviders.length > 0;
    // Get installed models that use importable providers (for pricing updates)
    const importableInstalledModels = useMemo(() => {
        const importableProviderIds = new Set(importableProviders.map((p) => p.id));
        return installedModels.filter((m) => m.providerId && importableProviderIds.has(m.providerId));
    }, [installedModels, importableProviders]);
    const handleOpenModelAction = useCallback(() => {
        if (hasImportableProviders) {
            setShowImportDialog(true);
        }
        else {
            setShowAddDialog(true);
        }
        setError(null);
    }, [hasImportableProviders]);
    // Primary model action, surfaced in the settings header bar on desktop
    // (a mobile fallback lives in the card toolbar below). Hidden until a
    // provider exists.
    const renderModelActionButton = useCallback((size = "sm") => providers.length > 0 ? (_jsxs(Button, { size: size, variant: "default", onClick: handleOpenModelAction, title: hasImportableProviders
            ? "Import models from provider"
            : "Add a model manually", children: [hasImportableProviders ? (_jsx(Cloud, { strokeWidth: 1, className: "mr-1 h-4 w-4" })) : (_jsx(Plus, { strokeWidth: 1, className: "mr-1 h-4 w-4" })), hasImportableProviders ? "Import models" : "Add model"] })) : null, [handleOpenModelAction, hasImportableProviders, providers.length]);
    // Register the model action (desktop top bar).
    const modelsHeaderActions = useMemo(() => {
        const modelActionButton = renderModelActionButton("sm");
        if (!modelActionButton)
            return null;
        return _jsx("div", { className: "flex items-center gap-2", children: modelActionButton });
    }, [renderModelActionButton]);
    useEffect(() => {
        if (!setSettingsHeaderActions)
            return;
        setSettingsHeaderActions(modelsHeaderActions);
        return () => {
            setSettingsHeaderActions(null);
        };
    }, [modelsHeaderActions, setSettingsHeaderActions]);
    // Group installed models by provider for efficient batch updates
    const installedModelsByProvider = useMemo(() => {
        const groups = {};
        importableInstalledModels.forEach((model) => {
            if (model.providerId) {
                if (!groups[model.providerId]) {
                    groups[model.providerId] = [];
                }
                groups[model.providerId].push(model);
            }
        });
        return groups;
    }, [importableInstalledModels]);
    // Update pricing for all installed models from importable providers
    const handleUpdateAllPricing = async () => {
        if (importableInstalledModels.length === 0) {
            setError("No models with supported providers installed to update");
            return;
        }
        try {
            setUpdatingAllPricing(true);
            setError(null);
            setUnavailableModels([]);
            let totalUpdated = 0;
            const allErrors = [];
            const unavailableByItemId = new Map();
            // Process each provider's models
            for (const [providerId, models] of Object.entries(installedModelsByProvider)) {
                const modelNames = models
                    .map((m) => m.name)
                    .filter(Boolean);
                if (modelNames.length === 0)
                    continue;
                const result = await setupWizardService.upsertProviderModels({
                    providerId,
                    selectedModelIds: modelNames,
                    mode: "updatePricing",
                });
                totalUpdated += result.updatedCount;
                allErrors.push(...result.errors);
                const missingProviderModelIds = extractMissingProviderModelIds(result.errors).map((modelId) => modelId.toLowerCase());
                if (missingProviderModelIds.length > 0) {
                    const missingProviderModelIdSet = new Set(missingProviderModelIds);
                    models.forEach((model) => {
                        if (model.id &&
                            model.name &&
                            missingProviderModelIdSet.has(model.name.toLowerCase())) {
                            unavailableByItemId.set(model.id, model);
                        }
                    });
                }
            }
            await loadData();
            setUnavailableModels(Array.from(unavailableByItemId.values()));
            if (allErrors.length > 0) {
                setError(`Updated ${totalUpdated} models. Errors: ${allErrors.join(", ")}`);
            }
        }
        catch (e) {
            setError(e?.message || "Failed to update pricing");
        }
        finally {
            setUpdatingAllPricing(false);
        }
    };
    const handleUpdate = async (model) => {
        if (!model.id || !model.key)
            return;
        try {
            setUpdating(model.id);
            setError(null);
            await setupWizardService.updateModel({
                modelId: model.id,
                sourceModelKey: model.key,
            });
            await loadData();
        }
        catch (e) {
            setError(e?.message || "Failed to update model");
        }
        finally {
            setUpdating(null);
        }
    };
    const handleDefaultModelsChange = async (nextChatModelId, nextImageModelId) => {
        if (!settingsExists) {
            setError("Parhelia settings item not found");
            return;
        }
        const previousChatModelId = defaultChatModelId;
        const previousImageModelId = defaultImageModelId;
        setDefaultChatModelId(nextChatModelId);
        setDefaultImageModelId(nextImageModelId);
        try {
            setSavingDefaults(true);
            setError(null);
            await setupWizardService.setDefaultModels({
                chatModelId: nextChatModelId,
                imageModelId: nextImageModelId,
            });
        }
        catch (e) {
            setDefaultChatModelId(previousChatModelId);
            setDefaultImageModelId(previousImageModelId);
            setError(e?.message || "Failed to save default models");
        }
        finally {
            setSavingDefaults(false);
        }
    };
    const handleDelete = async (model) => {
        if (!editContext || !model.id)
            return;
        try {
            setDeleting(model.id);
            setError(null);
            const deleted = await editContext.operations.deleteItems([
                {
                    id: model.id,
                    language: editContext.item?.language ??
                        editContext.currentItemDescriptor?.language ??
                        "en",
                    version: 0,
                },
            ]);
            if (deleted) {
                if (selectedModel?.id === model.id) {
                    setSelectedModel(null);
                    updateSelectedModelUrl(null);
                }
                await loadData();
            }
        }
        catch (e) {
            setError(e?.message || "Failed to delete model");
        }
        finally {
            setDeleting(null);
        }
    };
    const handleRemoveUnavailableModels = async () => {
        if (!editContext) {
            setError("Editor context is not available; cannot remove models.");
            return;
        }
        const unavailableIds = new Set(unavailableModels.map((model) => model.id));
        const modelsToRemove = installedModels.filter((model) => unavailableIds.has(model.id));
        if (modelsToRemove.length === 0) {
            setUnavailableModels([]);
            return;
        }
        try {
            setRemovingUnavailableModels(true);
            setError(null);
            const language = editContext.item?.language ??
                editContext.currentItemDescriptor?.language ??
                "en";
            const deleted = await editContext.operations.deleteItems(modelsToRemove.map((model) => ({
                id: model.id,
                language,
                version: 0,
            })));
            if (deleted) {
                const removedIds = new Set(modelsToRemove.map((model) => model.id));
                if (selectedModel && removedIds.has(selectedModel.id)) {
                    setSelectedModel(null);
                    updateSelectedModelUrl(null);
                }
                setUnavailableModels([]);
                await loadData();
            }
        }
        catch (e) {
            setError(e?.message || "Failed to remove unavailable models");
        }
        finally {
            setRemovingUnavailableModels(false);
        }
    };
    const openDuplicateDialog = (model) => {
        const segment = model.itemName?.trim() ||
            (model.key.includes("/") && model.key.split("/").length > 0
                ? model.key.split("/").pop()
                : model.name || model.key);
        setDuplicateItemName(`Copy of ${segment}`);
        setDuplicateDialogModel(model);
    };
    const handleConfirmDuplicate = async () => {
        if (!editContext || !duplicateDialogModel?.id) {
            return;
        }
        const parentId = duplicateDialogModel.parentId;
        if (!parentId) {
            return;
        }
        const name = duplicateItemName.trim();
        if (!name) {
            setError("Enter a name for the duplicate item");
            return;
        }
        const model = duplicateDialogModel;
        const lang = editContext.item?.language ??
            editContext.currentItemDescriptor?.language ??
            "en";
        try {
            setDuplicatingId(model.id);
            setError(null);
            const created = await editContext.operations.duplicateItem({ id: model.id, language: lang, version: 0 }, { id: parentId, language: lang, version: 0 }, name);
            if (!created) {
                setDuplicatingId(null);
                return;
            }
            setDuplicateDialogModel(null);
            const list = await loadData();
            if (list) {
                const createdId = String(created.id).replace(/[{}]/g, "").toLowerCase();
                const next = list.find((m) => m.id.replace(/[{}]/g, "").toLowerCase() === createdId);
                if (next) {
                    setSelectedModel(next);
                    updateSelectedModelUrl(next.id);
                }
            }
        }
        catch (e) {
            setError(e?.message || "Failed to duplicate model");
        }
        finally {
            setDuplicatingId(null);
        }
    };
    useEffect(() => {
        loadData();
    }, []);
    const handleNavigateToProviders = () => {
        editContext?.switchWorkspace("settings", {
            skipConfirmation: true,
            urlParams: { ccpanel: "ai-providers" },
        });
    };
    const chatModels = useMemo(() => installedModels
        .filter((model) => model.templateType === "Ai Chat Model")
        .sort((a, b) => getModelListLabel(a).localeCompare(getModelListLabel(b))), [installedModels]);
    const imageModels = useMemo(() => installedModels
        .filter((model) => model.templateType === "Ai Image Model")
        .sort((a, b) => getModelListLabel(a).localeCompare(getModelListLabel(b))), [installedModels]);
    const chatDefaultConfiguredModel = useMemo(() => chatModels.find((model) => normalizeModelId(model.id) === normalizeModelId(defaultChatModelId)), [chatModels, defaultChatModelId]);
    const imageDefaultConfiguredModel = useMemo(() => imageModels.find((model) => normalizeModelId(model.id) === normalizeModelId(defaultImageModelId)), [imageModels, defaultImageModelId]);
    const chatModelOptions = useMemo(() => {
        const options = [
            { value: "", label: "Not set" },
            ...chatModels.map((model) => ({
                value: model.id,
                label: getModelListLabel(model),
                description: model.providerName || undefined,
            })),
        ];
        if (defaultChatModelId && !chatDefaultConfiguredModel) {
            options.push({
                value: defaultChatModelId,
                label: "Configured model not found",
                description: defaultChatModelId,
            });
        }
        return options;
    }, [chatModels, defaultChatModelId, chatDefaultConfiguredModel]);
    const imageModelOptions = useMemo(() => {
        const options = [
            { value: "", label: "Not set" },
            ...imageModels.map((model) => ({
                value: model.id,
                label: getModelListLabel(model),
                description: model.providerName || undefined,
            })),
        ];
        if (defaultImageModelId && !imageDefaultConfiguredModel) {
            options.push({
                value: defaultImageModelId,
                label: "Configured model not found",
                description: defaultImageModelId,
            });
        }
        return options;
    }, [imageModels, defaultImageModelId, imageDefaultConfiguredModel]);
    // Filter models based on filter text
    const filteredModels = useMemo(() => {
        if (!filterText.trim()) {
            return installedModels;
        }
        const searchTerm = filterText.toLowerCase().trim();
        return installedModels.filter((model) => {
            const listLabel = getModelListLabel(model).toLowerCase();
            const modelName = (model.name || model.key || "").toLowerCase();
            const providerName = (model.providerName || "").toLowerCase();
            const templateType = (model.templateType || "").toLowerCase();
            const modelTypeLabel = getModelTypeLabel(model.templateType || "Ai Chat Model").toLowerCase();
            return (listLabel.includes(searchTerm) ||
                modelName.includes(searchTerm) ||
                providerName.includes(searchTerm) ||
                templateType.includes(searchTerm) ||
                modelTypeLabel.includes(searchTerm));
        });
    }, [installedModels, filterText]);
    // Group models by provider for display
    const groupModelsByProvider = useCallback((models) => {
        const groups = {};
        models.forEach((model) => {
            const providerId = model.providerId || "unknown";
            const provider = providers.find((p) => p.id === providerId);
            const providerName = model.providerName || provider?.name || "Unknown Provider";
            if (!groups[providerId]) {
                groups[providerId] = { providerName, models: [] };
            }
            groups[providerId].models.push(model);
        });
        // Sort models within each group by model type then name
        Object.values(groups).forEach((group) => {
            group.models.sort((a, b) => {
                const typeOrder = (a.templateType || "").localeCompare(b.templateType || "");
                if (typeOrder !== 0)
                    return typeOrder;
                return getModelListLabel(a).localeCompare(getModelListLabel(b));
            });
        });
        return groups;
    }, [providers]);
    const modelsByProvider = useMemo(() => groupModelsByProvider(filteredModels), [filteredModels, groupModelsByProvider]);
    const modelsMissingPricing = useMemo(() => {
        return installedModels.filter((model) => isPricingLineEmpty(model));
    }, [installedModels]);
    // Build the models list content
    const modelsListContent = (_jsxs(SettingsPanelLayout, { contentClassName: "flex flex-col gap-4", children: [modelsMissingPricing.length > 0 && (_jsxs("div", { className: "border-feedback-orange bg-feedback-orange-light text-feedback-orange flex items-start gap-3 rounded-lg border p-3 text-xs", children: [_jsx(AlertCircle, { strokeWidth: 1, className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { children: [modelsMissingPricing.length, " installed model", modelsMissingPricing.length !== 1 ? "s have" : " has", " missing or incomplete pricing information. Check the models list below to review and update pricing."] })] })), _jsxs(Card, { icon: _jsx(Sparkles, { strokeWidth: 1, className: "h-5 w-5" }), title: "Default models", description: "Choose the shared defaults used when a chat or image model is not configured elsewhere.", headerActions: savingDefaults ? (_jsxs("div", { className: "text-neutral-grey-50 flex items-center gap-2 text-xs", children: [_jsx(RefreshCw, { strokeWidth: 1, className: "h-3.5 w-3.5 animate-spin" }), "Saving..."] })) : undefined, children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-neutral-grey-50 flex items-center gap-2 text-xs font-normal", children: [_jsx(Brain, { className: "text-neutral-grey-50 h-3.5 w-3.5", strokeWidth: 1 }), "Default chat model"] }), _jsx(Select, { value: defaultChatModelId, onValueChange: (value) => void handleDefaultModelsChange(value, defaultImageModelId), options: chatModelOptions, placeholder: "Select a chat model", disabled: savingDefaults || !settingsExists, searchable: true, searchPlaceholder: "Search chat models..." }), _jsx("p", { className: "text-neutral-grey-50 text-xs", children: "Used by features that fall back to the settings-level chat model." })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-neutral-grey-50 flex items-center gap-2 text-xs font-normal", children: [_jsx(Image, { className: "text-neutral-grey-50 h-3.5 w-3.5", strokeWidth: 1 }), "Default image model"] }), _jsx(Select, { value: defaultImageModelId, onValueChange: (value) => void handleDefaultModelsChange(defaultChatModelId, value), options: imageModelOptions, placeholder: "Select an image model", disabled: savingDefaults || !settingsExists, searchable: true, searchPlaceholder: "Search image models..." }), _jsx("p", { className: "text-neutral-grey-50 text-xs", children: "Used by image generation and editing features that rely on the shared default." })] })] }), !settingsExists && (_jsx("div", { className: "border-feedback-orange bg-feedback-orange-light text-feedback-orange mt-4 rounded border p-3 text-xs", children: "Create the Parhelia settings item before saving default models." }))] }), _jsxs(Card, { icon: _jsx(Layers, { strokeWidth: 1, className: "h-5 w-5" }), title: _jsxs(_Fragment, { children: ["Installed models", " ", _jsxs("span", { className: "text-neutral-grey-50 font-normal", children: ["(", installedModels.length, ")"] })] }), description: "Models available to Parhelia, grouped by provider.", noPadding: true, headerActions: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "md:hidden", children: renderModelActionButton("sm") }), importableInstalledModels.length > 0 && (_jsxs(Button, { size: "sm", variant: "outline", onClick: handleUpdateAllPricing, disabled: updatingAllPricing, title: "Update pricing for all models from their provider APIs", children: [_jsx(RefreshCw, { strokeWidth: 1, className: `mr-1 h-4 w-4 ${updatingAllPricing ? "animate-spin" : ""}` }), "Update pricing"] }))] }), children: [_jsxs("div", { className: "border-border-default shrink-0 border-t px-4 py-3", children: [_jsx(SearchInput, { value: filterText, onChange: setFilterText, placeholder: "Filter by name, provider, or type...", className: "w-full" }), state !== "loading" && providers.length === 0 && (_jsx("div", { className: "border-feedback-orange bg-feedback-orange-light mt-2 rounded border p-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(AlertCircle, { strokeWidth: 1, className: "text-feedback-orange h-4 w-4 shrink-0" }), _jsx("div", { className: "text-feedback-orange flex-1 text-xs", children: "No providers configured. Configure at least one before adding models." }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handleNavigateToProviders, className: "text-xs", children: ["Go to Providers", _jsx(ArrowRight, { strokeWidth: 1, className: "ml-1 h-3 w-3" })] })] }) })), error && (_jsx("div", { className: "border-feedback-red bg-feedback-red-light text-feedback-red mt-2 rounded border p-2 text-xs whitespace-pre-wrap", children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "min-w-0 flex-1", children: error }), unavailableModels.length > 0 &&
                                            error.includes("not found in provider catalog") ? (_jsxs(Button, { size: "xs", variant: "outline", onClick: handleRemoveUnavailableModels, disabled: !editContext || removingUnavailableModels, className: "self-start bg-white", title: `Remove ${unavailableModels.length} unavailable model${unavailableModels.length === 1 ? "" : "s"}`, children: [removingUnavailableModels ? (_jsx(RefreshCw, { strokeWidth: 1, className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(DeleteIcon, { size: "sm" })), "Remove unavailable"] })) : null] }) }))] }), _jsx("div", { className: "px-4 py-3", children: filterText && filteredModels.length === 0 ? (_jsxs("div", { className: "text-neutral-grey-50 flex items-center justify-center py-8 text-sm", children: ["No models found matching ", `"${filterText}"`] })) : state === "success" &&
                            installedModels.length === 0 &&
                            providers.length > 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center px-6 py-12 text-center", children: [_jsx("div", { className: "bg-neutral-grey-5 mb-4 flex h-14 w-14 items-center justify-center rounded-full", children: _jsx(Brain, { className: "text-neutral-grey-50 h-7 w-7", strokeWidth: 1 }) }), _jsx("h3", { className: "text-neutral-grey-100 text-base font-medium", children: "No models installed yet" }), _jsx("p", { className: "text-neutral-grey-50 mt-1 max-w-md text-sm", children: hasImportableProviders
                                        ? "Import models from a configured provider to get started."
                                        : "Add a model manually to get started." }), _jsx("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: renderModelActionButton("default") })] })) : (_jsx("div", { className: "space-y-8", children: Object.entries(modelsByProvider).map(([providerId, { providerName, models }]) => {
                                if (models.length === 0)
                                    return null;
                                const provider = providers.find((p) => p.id === providerId);
                                return (_jsxs("div", { children: [_jsxs("div", { className: "mt-1 mb-3 flex items-center gap-2.5 px-1", children: [_jsx(ProviderIcon, { providerId: provider?.type || "", providerName: provider?.name || providerName, size: 24 }), _jsx("span", { className: "text-neutral-grey-100 text-sm font-medium", children: providerName }), _jsxs("span", { className: "text-neutral-grey-50 text-xs", children: [models.length, " model", models.length !== 1 ? "s" : ""] })] }), _jsx("div", { className: "divide-border-default divide-y", children: models.map((model) => {
                                                const status = model.status;
                                                const isSelected = selectedModel?.id === model.id;
                                                const showPricingLine = hasVisiblePricingSummary(model);
                                                const pricingSegments = buildPricingSegments(model);
                                                const contextSegments = buildContextSegments(model);
                                                const metaSegments = [
                                                    ...pricingSegments,
                                                    ...contextSegments,
                                                ];
                                                return (_jsxs("div", { role: "button", tabIndex: 0, onClick: () => handleOpenConfig(model), onKeyDown: (e) => {
                                                        if (e.key === "Enter" || e.key === " ") {
                                                            e.preventDefault();
                                                            handleOpenConfig(model);
                                                        }
                                                    }, className: `group focus-visible:ring-feedback-blue flex cursor-pointer items-center gap-3 px-3 py-3 text-sm transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-inset ${isSelected
                                                        ? "bg-neutral-grey-5"
                                                        : "hover:bg-neutral-grey-5 bg-white"}`, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx("span", { className: "text-neutral-grey-100 truncate font-normal", children: getModelListLabel(model) }), _jsx("span", { className: "bg-neutral-grey-5 badge-pad-sm text-neutral-grey-50 shrink-0 rounded text-[10px] font-medium", children: getModelTypeLabel(model.templateType || "Ai Chat Model") }), normalizeModelId(model.id) ===
                                                                            normalizeModelId(defaultChatModelId) && (_jsx("span", { className: "bg-feedback-green-light badge-pad-sm text-feedback-green shrink-0 rounded text-[10px] font-medium", children: "Default chat" })), normalizeModelId(model.id) ===
                                                                            normalizeModelId(defaultImageModelId) && (_jsx("span", { className: "bg-feedback-green-light badge-pad-sm text-feedback-green shrink-0 rounded text-[10px] font-medium", children: "Default image" })), status === "Outdated" && (_jsx("span", { className: "bg-feedback-orange-light badge-pad-sm text-feedback-orange shrink-0 rounded text-[10px] font-medium", children: "Update available" })), !showPricingLine && (_jsx("span", { className: "bg-feedback-orange-light badge-pad-sm text-feedback-orange shrink-0 rounded text-[10px] font-medium", title: getPricingWarningMessage(model), children: "Pricing missing" }))] }), metaSegments.length > 0 && (_jsx("div", { className: "text-neutral-grey-50 mt-0.5 truncate text-xs", children: metaSegments.join(" · ") }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [status === "Outdated" && (_jsx(Button, { size: "sm", variant: "ghost", onClick: (e) => {
                                                                        e.stopPropagation();
                                                                        handleUpdate(model);
                                                                    }, disabled: updating === model.id, title: "Update model", children: updating === model.id ? (_jsx(RefreshCw, { strokeWidth: 1, className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(Upload, { strokeWidth: 1, className: "h-3.5 w-3.5" })) })), _jsx(Button, { size: "sm", variant: "ghost", onClick: (e) => {
                                                                        e.stopPropagation();
                                                                        openDuplicateDialog(model);
                                                                    }, disabled: !editContext ||
                                                                        !model.parentId ||
                                                                        duplicatingId === model.id, title: "Duplicate model", className: "opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100", children: duplicatingId === model.id ? (_jsx(RefreshCw, { strokeWidth: 1, className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(Copy, { strokeWidth: 1, className: "h-3.5 w-3.5" })) }), _jsx(Button, { size: "sm", variant: "ghost", onClick: (e) => {
                                                                        e.stopPropagation();
                                                                        handleDelete(model);
                                                                    }, disabled: deleting === model.id, title: "Delete model", className: "text-neutral-grey-50 hover:bg-neutral-grey-5 hover:text-neutral-grey-50 opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100", children: deleting === model.id ? (_jsx(RefreshCw, { strokeWidth: 1, className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(DeleteIcon, { size: "md" })) })] })] }, model.id));
                                            }) })] }, providerId));
                            }) })) })] }), _jsx(AddModelDialog, { open: showAddDialog, onOpenChange: setShowAddDialog, onSuccess: loadData, providers: providers, installedModels: installedModels }), _jsx(ImportModelDialog, { open: showImportDialog, onOpenChange: setShowImportDialog, onSuccess: loadData, importableProviders: importableProviders, providerTypes: providerTypes }), _jsx(Dialog, { open: duplicateDialogModel !== null, onOpenChange: (open) => {
                    if (!open)
                        setDuplicateDialogModel(null);
                }, children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { className: "pr-14", children: [_jsx(DialogTitle, { children: "Duplicate model" }), _jsx(DialogDescription, { children: "New Sitecore item name. Field values (including model name and reasoning) are copied; adjust them in Configure after duplicating." })] }), _jsxs("div", { className: "space-y-2 px-8 pb-6", children: [_jsx("label", { htmlFor: "duplicate-model-item-name", className: "text-neutral-grey-100 block text-sm font-medium", children: "Item name" }), _jsx(Input, { id: "duplicate-model-item-name", value: duplicateItemName, onChange: (e) => setDuplicateItemName(e.target.value), onKeyDown: (e) => {
                                        if (e.key === "Enter") {
                                            e.preventDefault();
                                            void handleConfirmDuplicate();
                                        }
                                    }, autoFocus: true })] }), _jsxs(DialogFooter, { className: "border-border-default border-t pt-4", children: [_jsx(Button, { type: "button", variant: "outline", className: "min-h-9 min-w-22", onClick: () => setDuplicateDialogModel(null), children: "Cancel" }), _jsx(Button, { type: "button", className: "min-h-9 min-w-22", onClick: () => void handleConfirmDuplicate(), disabled: !duplicateItemName.trim() ||
                                        (duplicateDialogModel != null &&
                                            duplicatingId === duplicateDialogModel.id), children: duplicateDialogModel != null &&
                                        duplicatingId === duplicateDialogModel.id ? (_jsxs(_Fragment, { children: [_jsx(RefreshCw, { strokeWidth: 1, className: "mr-1 h-4 w-4 animate-spin" }), "Duplicating\u2026"] })) : ("Duplicate") })] })] }) })] }));
    // Build splitter panels - only show config panel when a model is selected
    const panels = selectedModel?.id
        ? [
            {
                name: "models-list",
                defaultSize: "auto",
                content: modelsListContent,
                className: "overflow-hidden",
            },
            {
                name: "model-config",
                defaultSize: 400,
                content: (_jsx(ModelConfigPanel, { itemId: selectedModel.id, title: `Configure: ${getModelListLabel(selectedModel) || "Model"}`, onClose: handleCloseConfig, onModelRenamed: async () => {
                        await loadData();
                    } }, selectedModel.id)),
                className: "overflow-hidden",
            },
        ]
        : [
            {
                name: "models-list",
                defaultSize: "auto",
                content: modelsListContent,
                className: "overflow-hidden",
            },
        ];
    return (_jsx(Splitter, { panels: panels, localStorageKey: "models-panel-splitter", direction: "horizontal", className: "h-full" }));
}
export default ModelsPanel;
//# sourceMappingURL=ModelsPanel.js.map