import { h } from "@stencil/core";
import { pathsEqual } from "./rule-operations";
import { createSearcher, resolveItem, } from "./view-helpers";
const CHIP_TOKEN_PREFIX = '__chip__:';
function chipTokenFor(index) {
    return `${CHIP_TOKEN_PREFIX}${index}`;
}
// The address of a chip inside a group is the group's path followed
// by a two-segment suffix `['rules', index]`. Build and parse via
// these helpers so both sides stay in sync.
const CHIP_PATH_SUFFIX_LENGTH = 2;
function buildChipPath(groupPath, index) {
    return [...groupPath, 'rules', index];
}
function parentGroupPath(chipPath) {
    return chipPath.slice(0, -CHIP_PATH_SUFFIX_LENGTH);
}
/**
 * If `node` is a `ref` or a `not(ref)`, return the underlying ref
 * together with its negation flag. Used to decide whether a child of a
 * group renders as a chip (this returns non-null) or as a nested box
 * (this returns null).
 * @param node
 */
export function extractRef(node) {
    if (node.type === 'ref') {
        return { ref: node, isNegated: false };
    }
    if (node.type === 'not' && node.rule.type === 'ref') {
        return { ref: node.rule, isNegated: true };
    }
    return null;
}
export const GroupChipsView = ({ groupNode, groupPath, chipChildren, ctx }) => {
    const chipsByToken = new Map();
    const value = chipChildren.map((entry) => {
        const token = chipTokenFor(entry.index);
        chipsByToken.set(token, entry);
        const base = resolveItem(ctx.primitiveOptions, entry.ref.id);
        return Object.assign(Object.assign({}, base), { text: entry.isNegated ? `¬ ${base.text}` : base.text, value: token });
    });
    const searcher = createSearcher(ctx.primitiveOptions);
    const openChip = findOpenChip(chipChildren, groupPath, ctx.openChipPath);
    const delimiter = groupNode.type === 'all' ? '&' : '|';
    return (h("limel-popover", { class: "rule-node__chips-popover", open: openChip !== null, openDirection: "bottom-start", onClose: ctx.onPopoverClose }, h("div", { slot: "trigger", class: "rule-node__chips" }, h("limel-picker", { multiple: true, label: ctx.translator.get('webclient.rule-editor.rules-label'), value: value, searcher: searcher, delimiter: delimiter, readonly: ctx.readonly, disabled: ctx.disabled, onChange: chipsChangeHandler(groupNode, groupPath, chipsByToken, ctx), onInteract: chipInteractHandler(groupPath, chipsByToken, ctx) })), h("div", null, renderChipPopoverContent(openChip, groupPath, ctx))));
};
function renderChipPopoverContent(openChip, groupPath, ctx) {
    if (!openChip) {
        return null;
    }
    const openChipPath = buildChipPath(groupPath, openChip.index);
    return (h("limebb-rule-chip-popover", { platform: ctx.platform, context: ctx.context, refNode: openChip.ref, isNegated: openChip.isNegated, metadata: ctx.metadataById.get(openChip.ref.id), readonly: ctx.readonly, disabled: ctx.disabled, isMutable: ctx.isMutable, onNegate: chipNegateHandler(openChip, openChipPath, ctx), onUpdateArgs: chipArgsHandler(openChip, openChipPath, ctx), onDeleteChip: chipDeleteHandler(openChipPath, ctx) }));
}
const chipsChangeHandler = (groupNode, groupPath, chipsByToken, ctx) => (event) => {
    event.stopPropagation();
    const items = Array.isArray(event.detail) ? event.detail : [];
    const openIndex = openChipIndexInGroup(groupPath, ctx.openChipPath);
    if (openIndex !== null) {
        const openToken = chipTokenFor(openIndex);
        if (items.every((item) => item.value !== openToken)) {
            ctx.onPopoverClose();
        }
    }
    const newChipNodes = items.flatMap((item) => {
        if (typeof item.value !== 'string') {
            return [];
        }
        const existing = chipsByToken.get(item.value);
        if (existing) {
            return [
                existing.isNegated
                    ? { type: 'not', rule: existing.ref }
                    : existing.ref,
            ];
        }
        return [{ type: 'ref', id: item.value }];
    });
    const nestedGroupRules = groupNode.rules.filter((rule) => extractRef(rule) === null);
    ctx.onReplaceNode(groupPath, Object.assign(Object.assign({}, groupNode), { rules: [...newChipNodes, ...nestedGroupRules] }));
};
const chipInteractHandler = (groupPath, chipsByToken, ctx) => (event) => {
    event.stopPropagation();
    const token = interactedToken(event.detail);
    const chip = token ? chipsByToken.get(token) : undefined;
    if (!chip) {
        return;
    }
    ctx.onChipInteract([...groupPath, 'rules', chip.index]);
};
const chipNegateHandler = (openChip, openChipPath, ctx) => (event) => {
    event.stopPropagation();
    const next = event.detail
        ? { type: 'not', rule: openChip.ref }
        : openChip.ref;
    ctx.onReplaceNode(openChipPath, next);
};
const chipArgsHandler = (openChip, openChipPath, ctx) => (event) => {
    event.stopPropagation();
    const argsPath = openChip.isNegated
        ? [...openChipPath, 'rule']
        : openChipPath;
    ctx.onUpdateArgs(argsPath, event.detail);
};
const chipDeleteHandler = (openChipPath, ctx) => () => {
    ctx.onDelete(openChipPath);
};
/**
 * `limel-picker` declares its `interact` event as
 * `EventEmitter<ListItem<PickerValue>>` but the runtime actually emits
 * the chip's value (a string) — see picker.tsx's handleInteract. This
 * unwraps either shape into the string token.
 * @param detail
 */
function interactedToken(detail) {
    if (typeof detail === 'string') {
        return detail;
    }
    if (detail && typeof detail === 'object' && 'value' in detail) {
        const value = detail.value;
        return typeof value === 'string' ? value : undefined;
    }
    return undefined;
}
function findOpenChip(chipChildren, groupPath, openChipPath) {
    var _a;
    const index = openChipIndexInGroup(groupPath, openChipPath);
    if (index === null) {
        return null;
    }
    return (_a = chipChildren.find((entry) => entry.index === index)) !== null && _a !== void 0 ? _a : null;
}
function openChipIndexInGroup(groupPath, openChipPath) {
    if (!openChipPath || openChipPath.length < CHIP_PATH_SUFFIX_LENGTH) {
        return null;
    }
    if (!pathsEqual(parentGroupPath(openChipPath), groupPath)) {
        return null;
    }
    const lastKey = openChipPath.at(-1);
    return typeof lastKey === 'number' ? lastKey : null;
}
