import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
import { bodyWidths, columnWidths, decideLayout, PREVIEW_PADDING_LEFT, STACKED_GAP_ROWS, } from "./preview-layout-decider.js";
// ----- Re-exports for test imports — keep `./preview-pane.js` as the public surface -----
export { MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE, MAX_PREVIEW_HEIGHT_STACKED, NO_PREVIEW_TEXT, NOTES_AFFORDANCE_OVERHEAD, } from "./markdown-content-cache.js";
export { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from "./preview-block-renderer.js";
export { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL, BORDER_VERTICAL_OVERHEAD, BOX_MIN_CONTENT_WIDTH, renderBorderedBox, stripFenceMarkers, } from "./preview-box-renderer.js";
export { PREVIEW_COLUMN_GAP, PREVIEW_MIN_WIDTH, PREVIEW_PADDING_LEFT, STACKED_GAP_ROWS, } from "./preview-layout-decider.js";
/**
 * Thin layout composer. Receives `selectedIndex` / `focused` / `notesVisible`
 * via `setProps` per tick (computed by `selectPreviewPaneProps` from canonical
 * state). Delegates option-side rendering to `OptionListView` (which still
 * owns its render-time state — input buffer, confirmedIndex) and preview-side
 * rendering to `PreviewBlockRenderer` (which owns the markdown cache and
 * bordered-box composition).
 *
 * `naturalHeight` and `maxNaturalHeight` query both children's heights; `render`
 * combines them via `decideLayout` (mode threaded into both calls — never
 * re-derived).
 */
export class PreviewPane {
    constructor(config) {
        /**
         * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`
         * before any render. Initialized to a throwing sentinel so missing injection is a hard fail
         * rather than a silent fallback to a magic constant — render is illegal until injected.
         */
        this.globalLeftWidth = () => {
            throw new Error("PreviewPane.setGlobalLeftWidth must be called before render()");
        };
        this.question = config.question;
        this.getTerminalWidth = config.getTerminalWidth;
        this.optionListView = config.optionListView;
        this.previewBlock = config.previewBlock;
        this.props = { notesVisible: false, selectedIndex: 0, focused: false };
    }
    setGlobalLeftWidth(getter) {
        this.globalLeftWidth = getter;
    }
    getAdaptiveLeft(paneWidth) {
        return this.globalLeftWidth(paneWidth);
    }
    setProps(props) {
        this.props = props;
    }
    handleInput(_data) { }
    invalidate() {
        this.previewBlock.invalidate();
        this.optionListView.invalidate();
    }
    render(width) {
        if (this.question.multiSelect === true)
            return this.optionListView.render(width);
        // Spec: hide the preview pane entirely when no option carries a `preview`.
        if (!this.previewBlock.hasAnyPreview())
            return this.optionListView.render(width);
        const mode = decideLayout(this.getTerminalWidth(), width);
        if (mode === "side-by-side")
            return this.renderSideBySide(width, mode);
        // Stacked: options + blank gap + preview block.
        return [
            ...this.optionListView.render(width),
            ...Array(STACKED_GAP_ROWS).fill(""),
            ...this.previewBlock.renderBlock(width, this.props.selectedIndex, mode, this.props.focused, this.props.notesVisible),
        ];
    }
    naturalHeight(width) {
        if (this.question.multiSelect === true)
            return this.optionListView.render(width).length;
        if (!this.previewBlock.hasAnyPreview())
            return this.optionListView.render(width).length;
        const mode = decideLayout(this.getTerminalWidth(), width);
        const adaptiveLeft = this.getAdaptiveLeft(width);
        const { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);
        const optionsHeight = this.optionListView.render(optionsWidth).length;
        const previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);
        if (mode === "side-by-side")
            return Math.max(optionsHeight, previewBlockHeight);
        return optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;
    }
    maxNaturalHeight(width) {
        if (this.question.multiSelect === true)
            return this.optionListView.render(width).length;
        if (!this.previewBlock.hasAnyPreview())
            return this.optionListView.render(width).length;
        const mode = decideLayout(this.getTerminalWidth(), width);
        const adaptiveLeft = this.getAdaptiveLeft(width);
        const { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);
        const optionsHeight = this.optionListView.render(optionsWidth).length;
        let maxPreviewBlock = 0;
        for (let i = 0; i < this.question.options.length; i++) {
            const h = this.previewBlock.blockHeight(previewWidth, i, mode);
            if (h > maxPreviewBlock)
                maxPreviewBlock = h;
        }
        if (mode === "side-by-side")
            return Math.max(optionsHeight, maxPreviewBlock);
        return optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;
    }
    renderSideBySide(width, mode) {
        const adaptiveLeft = this.getAdaptiveLeft(width);
        const { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);
        const leftLines = this.optionListView.render(leftWidth);
        const rightLines = this.renderPaddedPreviewLines(rightWidth, mode);
        const rows = Math.max(leftLines.length, rightLines.length);
        const gapStr = " ".repeat(gap);
        const out = [];
        for (let i = 0; i < rows; i++) {
            const leftRaw = leftLines[i] ?? "";
            const rightRaw = rightLines[i] ?? "";
            const leftClamped = truncateToWidth(leftRaw, leftWidth, "");
            const leftPad = " ".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));
            const joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;
            out.push(truncateToWidth(joined, width, ""));
        }
        return out;
    }
    renderPaddedPreviewLines(colWidth, mode) {
        const inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);
        const contentLines = this.previewBlock.renderBlock(inner, this.props.selectedIndex, mode, this.props.focused, this.props.notesVisible);
        const pad = " ".repeat(PREVIEW_PADDING_LEFT);
        return contentLines.map((l) => (l === "" ? "" : `${pad}${l}`));
    }
}
//# sourceMappingURL=preview-pane.js.map