import { getMarkdownTheme } from "../../../../modes/interactive/theme/theme.js";
import { Input } from "@earendil-works/pi-tui";
import { globalBinding, perTabBinding, } from "../view/component-binding.js";
import { ChatRowView } from "../view/components/chat-row-view.js";
import { MultiSelectView } from "../view/components/multi-select-view.js";
import { OptionListView } from "../view/components/option-list-view.js";
import { PreviewBlockRenderer } from "../view/components/preview/preview-block-renderer.js";
import { crossTabLeftWidthWithDonation } from "../view/components/preview/preview-layout-decider.js";
import { PreviewPane } from "../view/components/preview/preview-pane.js";
import { SubmitPicker } from "../view/components/submit-picker.js";
import { TabBar } from "../view/components/tab-bar.js";
import { DialogView } from "../view/dialog-builder.js";
import { QuestionnairePropsAdapter } from "../view/props-adapter.js";
import { ROW_INTENT_META } from "./row-intent.js";
import { selectActivePreviewPaneIndex } from "./selectors/derivations.js";
import { selectChatRowProps, selectDialogProps, selectMultiSelectProps, selectOptionListProps, selectPreviewPaneProps, selectSubmitPickerProps, selectTabBarProps, } from "./selectors/projections.js";
function previewBodyHeights(pane) {
    return (width) => ({
        current: pane.naturalHeight(width),
        max: pane.maxNaturalHeight(width),
    });
}
function multiSelectBodyHeights(view) {
    return (width) => {
        const h = view.naturalHeight(width);
        return { current: h, max: h };
    };
}
const isActiveTab = (s, ctx) => ctx.i === selectActivePreviewPaneIndex(s.currentTab, ctx.totalQuestions);
/**
 * Pure factory: assembles every TUI component, the props adapter, and a
 * lifecycle handle. Session-state dependencies arrive via `getCurrentTab` and
 * the `inputBuffer` cell. Initial paint is delegated to
 * `adapter.apply(initialState)` (called by the session at construction-end);
 * no selector is invoked here.
 */
export function buildQuestionnaire(config) {
    return new QuestionnaireBuilder(config).build();
}
/**
 * Programming-by-intention assembly: each private method names one
 * construction step from `build()`. Read top-down; the class is build-only and
 * discarded once the handle is returned.
 */
class QuestionnaireBuilder {
    constructor(config) {
        this.markdownTheme = getMarkdownTheme();
        this.notesInput = new Input();
        this.inlineInput = new Input();
        this.getTerminalWidth = () => this.tui.terminal.columns;
        this.tui = config.tui;
        this.theme = config.theme;
        this.questions = config.questions;
        this.itemsByTab = config.itemsByTab;
        this.isMulti = config.isMulti;
        this.initialState = config.initialState;
        this.getCurrentTab = config.getCurrentTab;
        this.selectTheme = this.makeSelectTheme();
        this.chatRow = new ChatRowView({
            item: { kind: "chat", label: ROW_INTENT_META.chat.label },
            theme: this.selectTheme,
        });
    }
    build() {
        const tabs = this.buildTabComponents();
        this.injectGlobalLeftWidth(tabs);
        const submitPicker = this.buildSubmitPicker();
        const tabBar = this.buildTabBar();
        const heights = this.buildHeightComputers(tabs);
        const dialog = this.buildDialog(tabs, submitPicker, tabBar, heights);
        const globalBindings = this.buildGlobalBindings(dialog, submitPicker, tabBar);
        const perTabBindings = this.buildPerTabBindings();
        const adapter = this.buildAdapter(tabs, globalBindings, perTabBindings);
        return this.handle(adapter, dialog);
    }
    makeSelectTheme() {
        const t = this.theme;
        return {
            selectedText: (s) => t.fg("accent", t.bold(s)),
            description: (s) => t.fg("muted", s),
            scrollInfo: (s) => t.fg("dim", s),
        };
    }
    buildTabComponents() {
        return this.questions.map((q, i) => this.buildTabFor(q, i));
    }
    buildTabFor(question, index) {
        const optionList = new OptionListView({
            items: this.itemsByTab[index] ?? [],
            theme: this.selectTheme,
        });
        const previewBlock = new PreviewBlockRenderer({
            question,
            theme: this.theme,
            markdownTheme: this.markdownTheme,
        });
        const preview = new PreviewPane({
            question,
            getTerminalWidth: this.getTerminalWidth,
            optionListView: optionList,
            previewBlock,
        });
        const multiSelect = question.multiSelect ? new MultiSelectView(this.theme, question) : undefined;
        const bodyHeights = this.buildBodyHeights(question, preview, multiSelect);
        return { optionList, preview, multiSelect, bodyHeights };
    }
    buildBodyHeights(question, preview, multiSelect) {
        return question.multiSelect ? multiSelectBodyHeights(multiSelect) : previewBodyHeights(preview);
    }
    /**
     * Compute cross-tab max adaptive left width and inject into each PreviewPane.
     * Mirrors buildHeightComputers pattern — iterates all tabs, takes max.
     * Called after buildTabComponents, before buildDialog (so setGlobalLeftWidth
     * is set before any rendering occurs).
     */
    injectGlobalLeftWidth(tabs) {
        const questions = this.questions;
        const itemsByTab = this.itemsByTab;
        const tabsDescriptor = questions.map((q) => ({ multiSelect: q.multiSelect }));
        const globalLeftWidth = (paneWidth) => crossTabLeftWidthWithDonation(tabsDescriptor, itemsByTab, questions, paneWidth);
        for (const tab of tabs) {
            tab.preview.setGlobalLeftWidth(globalLeftWidth);
        }
    }
    buildSubmitPicker() {
        return this.isMulti ? new SubmitPicker(this.theme) : undefined;
    }
    buildTabBar() {
        return this.isMulti ? new TabBar(this.theme) : undefined;
    }
    buildHeightComputers(tabs) {
        const global = (width) => {
            let max = 0;
            for (const tab of tabs) {
                const h = tab.bodyHeights(width).max;
                if (h > max)
                    max = h;
            }
            return Math.max(1, max);
        };
        const current = (width) => {
            const idx = Math.min(this.getCurrentTab(), tabs.length - 1);
            return Math.max(0, tabs[idx]?.bodyHeights(width).current ?? 0);
        };
        return { global, current };
    }
    pickInitialActivePreview(tabs) {
        const idx = selectActivePreviewPaneIndex(this.initialState.currentTab, this.questions.length);
        return tabs[idx]?.preview ?? tabs[0].preview;
    }
    buildDialog(tabs, submitPicker, tabBar, heights) {
        return new DialogView({
            theme: this.theme,
            questions: this.questions,
            tabBar,
            notesInput: this.notesInput,
            chatRow: this.chatRow,
            isMulti: this.isMulti,
            tabsByIndex: tabs,
            submitPicker,
            getBodyHeight: heights.global,
            getCurrentBodyHeight: heights.current,
        }, { state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) });
    }
    buildGlobalBindings(dialog, submitPicker, tabBar) {
        return [
            globalBinding({ component: dialog, select: selectDialogProps }),
            globalBinding({ component: this.chatRow, select: selectChatRowProps }),
            ...(submitPicker ? [globalBinding({ component: submitPicker, select: selectSubmitPickerProps })] : []),
            ...(tabBar ? [globalBinding({ component: tabBar, select: selectTabBarProps })] : []),
        ];
    }
    buildPerTabBindings() {
        return [
            perTabBinding({
                resolve: (tab) => tab.optionList,
                predicate: isActiveTab,
                select: selectOptionListProps,
            }),
            perTabBinding({
                resolve: (tab) => tab.preview,
                predicate: isActiveTab,
                select: selectPreviewPaneProps,
            }),
            perTabBinding({
                resolve: (tab) => tab.multiSelect,
                select: selectMultiSelectProps,
            }),
        ];
    }
    buildAdapter(tabs, globalBindings, perTabBindings) {
        return new QuestionnairePropsAdapter({
            tui: this.tui,
            questions: this.questions,
            itemsByTab: this.itemsByTab,
            tabsByIndex: tabs,
            inlineInput: this.inlineInput,
            globalBindings,
            perTabBindings,
            extraInvalidatables: [this.notesInput],
        });
    }
    handle(adapter, dialog) {
        return {
            adapter,
            notesInput: this.notesInput,
            inlineInput: this.inlineInput,
            render: (w) => dialog.render(w),
            invalidate: () => adapter.invalidate(),
        };
    }
}
//# sourceMappingURL=build-questionnaire.js.map