import { useState } from "react";
import { useEffect, useRef } from "react";
import { localStorageService } from "./services/localStorageService";
// Hook
export function useEventListenerExt(eventName, handler, element, capture = false) {
    // Create a ref that stores handler
    const savedHandler = useRef(undefined);
    // Update ref.current value if handler changes.
    // This allows our effect below to always get latest handler ...
    // ... without us needing to pass it in effect deps array ...
    // ... and potentially cause effect to re-run every render.
    useEffect(() => {
        savedHandler.current = handler;
    }, [handler]);
    useEffect(() => {
        // Make sure element supports addEventListener
        // On
        const isSupported = element && element.addEventListener;
        if (!isSupported)
            return;
        // Create event listener that calls handler function stored in ref
        const eventListener = (event) => savedHandler.current(event);
        // Add event listener
        element.addEventListener(eventName, eventListener, { capture: capture });
        // Remove event listener on cleanup
        return () => {
            element.removeEventListener(eventName, eventListener);
        };
    }, [eventName, element]);
}
export const useLocalStorage = (storageKey, fallbackState) => {
    const [value, setValue] = useState(() => localStorageService.getItem(storageKey) ?? fallbackState);
    useEffect(() => {
        localStorageService.setItem(storageKey, value);
    }, [value, storageKey]);
    return [value, setValue];
};
// Utility functions for managing collapsed sections
export const useCollapsedSections = () => {
    const [collapsedSections, setCollapsedSections] = useLocalStorage("editor.collapsedSections", []);
    const isSectionCollapsed = (sectionTitle) => {
        return collapsedSections.includes(sectionTitle);
    };
    const toggleSection = (sectionTitle) => {
        setCollapsedSections((prev) => {
            if (prev.includes(sectionTitle)) {
                // Remove from collapsed sections (expand)
                return prev.filter((title) => title !== sectionTitle);
            }
            else {
                // Add to collapsed sections (collapse)
                return [...prev, sectionTitle];
            }
        });
    };
    return {
        isSectionCollapsed,
        toggleSection,
    };
};
export const extractItemIdFromItemUri = (uri) => {
    const match = uri.match(/\{[A-F0-9-]+\}/i);
    return match
        ? match[0].replace(/[{}]/g, "").toLowerCase()
        : uri.toLowerCase();
};
export function isFieldActionExecuting(field, editContext) {
    const fieldItem = field.descriptor.item;
    if (!fieldItem) {
        return false;
    }
    var action = editContext.activeFieldActions.find((x) => x.field.fieldId == field.id &&
        x.field.item?.id == fieldItem.id &&
        x.field.item?.language == fieldItem.language &&
        x.field.item?.version == fieldItem.version);
    return action?.state == "running";
}
export function getItemDescriptor(item) {
    if (!item)
        throw new Error("Item is null");
    if ("descriptor" in item) {
        return item.descriptor;
    }
    // If it's already an ItemDescriptor, preserve all properties including database and path
    return {
        id: item.id,
        language: item.language,
        version: item.version,
        //  name: item!.name,
        //  path: item!.path,
        database: item.database !== undefined &&
            item.database !== null &&
            item.database !== "master"
            ? item.database
            : undefined,
    };
}
export function getAbsoluteIconUrl(url) {
    //return configuration.services.editorService.baseUrl + url;
    return url;
}
export function findParentWithAttribute(element, attr) {
    while (element && element !== document.documentElement) {
        if (element.hasAttribute(attr)) {
            return element;
        }
        element = element.parentElement;
    }
    return null;
}
export function parsePlaceholderKey(key) {
    const placeholderKeyComponents = key.split("_");
    const placeholderName = placeholderKeyComponents.length === 1
        ? placeholderKeyComponents[0]
        : placeholderKeyComponents[1];
    const parentId = placeholderKeyComponents.length === 1
        ? undefined
        : placeholderKeyComponents[0];
    return {
        name: placeholderName,
        componentId: parentId,
    };
}
// export function getFieldRawValue(field: Field, editContext: EditContextType) {
//   return (
//     getModifiedField(field.descriptor, editContext)?.rawValue ??
//     field.rawValue ??
//     field.value
//   );
// }
// export function getFieldValue(field: Field, editContext: EditContextType) {
//   return getModifiedField(field.descriptor, editContext)?.value ?? field.value;
// }
// export function getModifiedField(
//   field: FieldDescriptor,
//   editContext: EditContextType
// ) {
//   const modifiedField = editContext.modifiedFields.find(
//     (x) =>
//       x.field.fieldId === field.fieldId &&
//       x.field.item.id === field.item.id &&
//       x.field.item.language === field.item.language &&
//       x.field.item.version === field.item.version
//   );
//   return modifiedField;
// }
// export function getModifiedFieldValue(
//   field: FieldDescriptor,
//   editContext: EditContextType
// ) {
//   return getModifiedField(field, editContext)?.value;
// }
// export function setModifiedFieldValue(
//   field: FieldDescriptor,
//   value: string,
//   editContext: EditContextType
// ) {
//   const modifiedValue = editContext.modifiedFields.find(
//     (x) =>
//       x.field.fieldId === field.fieldId &&
//       x.field.item.id === field.item.id &&
//       x.field.item.language === field.item.language &&
//       x.field.item.version === field.item.version
//   );
//   if (modifiedValue) {
//     modifiedValue.value = value;
//   } else {
//     editContext.modifiedFields.push({
//       field: field,
//       value: value,
//     });
//   }
//   editContext.setModifiedFields([...editContext.modifiedFields]);
// }
export function hasFieldLock(field, editContext) {
    if (!editContext.lockedField) {
        return false;
    }
    return (editContext.lockedField.fieldId === field.fieldId &&
        editContext.lockedField.item.id === field.item.id &&
        editContext.lockedField.item.language === field.item.language &&
        editContext.lockedField.item.version === field.item.version);
}
export function getSessionWithFieldLock(field, editContext) {
    return editContext.activeSessions.find((s) => s.fieldLock?.fieldId === field.fieldId &&
        s.fieldLock?.item.id === field.item.id &&
        s.fieldLock?.item.language === field.item.language &&
        s.fieldLock?.item.version === field.item.version);
}
export function getFieldDescriptorFromElement(element) {
    return {
        item: {
            id: element.getAttribute("data-itemid"),
            version: parseInt(element.getAttribute("data-version")),
            language: element.getAttribute("data-language"),
        },
        fieldId: element.getAttribute("data-fieldid"),
    };
}
export function findNearestEditableComponentId(startElement) {
    // If pages expose component id directly on content wrappers, this is the
    // most precise and cheapest lookup.
    let current = startElement;
    while (current) {
        const directComponentId = current.getAttribute("data-component-id");
        if (directComponentId && isEditableComponent(current)) {
            return directComponentId;
        }
        current = current.parentElement;
    }
    // Fast path: prefer the closest ancestor markers. This is more reliable for
    // nested/SXA markup where a broad backward scan can resolve to a parent
    // rendering before considering the clicked node's own container identity.
    current = startElement;
    while (current) {
        if (current.hasAttribute("data-component-start") &&
            !current.hasAttribute("data-skip-component") &&
            isEditableComponent(current)) {
            return current.getAttribute("data-component-start") ?? undefined;
        }
        current = current.parentElement;
    }
    // Some SXA/content field wrappers expose only data-itemid. Prefer the
    // nearest editable one before broad scans to avoid falling back to page-level
    // selections when markers are not present in active tab panes.
    current = startElement;
    while (current) {
        const itemId = current.getAttribute("data-itemid");
        if (itemId && isEditableComponent(current)) {
            return itemId;
        }
        current = current.parentElement;
    }
    // If the clicked DOM subtree exposes sc_item directly, prefer the nearest
    // ancestor sc_item before falling back to a full backward document scan.
    current = startElement;
    while (current) {
        if (current.getAttribute("sc_item") && isEditableComponent(current)) {
            return extractItemIdFromItemUri(current.getAttribute("sc_item"));
        }
        current = current.parentElement;
    }
    // First, try to resolve by explicit component markers. This guarantees we
    // select the actual rendering instance even when multiple components share
    // the same datasource item (same sc_item value).
    let element = startElement;
    while (element) {
        if (element.hasAttribute("data-component-start") &&
            !element.hasAttribute("data-skip-component") &&
            isEditableComponent(element)) {
            return element.getAttribute("data-component-start") ?? undefined;
        }
        const prev = element.previousElementSibling;
        if (!prev) {
            element = element.parentElement;
        }
        else {
            element = prev;
        }
    }
    // Fallback: if no component marker was found (e.g. legacy markup or
    // non-component areas), fall back to sc_item-based resolution which selects
    // by datasource item.
    element = startElement;
    while (element) {
        if (element.getAttribute("sc_item") && isEditableComponent(element)) {
            return extractItemIdFromItemUri(element.getAttribute("sc_item"));
        }
        const prev = element.previousElementSibling;
        if (!prev) {
            element = element.parentElement;
        }
        else {
            element = prev;
        }
    }
    return;
}
function isEditableComponent(element) {
    return element.getAttribute("data-editable") !== "false";
}
function getAccessibleIframeDocument(iframe) {
    if (!iframe)
        return null;
    try {
        return iframe.contentDocument || iframe.contentWindow?.document || null;
    }
    catch {
        return null;
    }
}
export function findFieldElement(iframe, fieldDescriptor) {
    const body = getAccessibleIframeDocument(iframe)?.body;
    if (!body)
        return undefined;
    const fieldElement = body.querySelector("[data-itemid='" +
        fieldDescriptor.item.id +
        "'][data-fieldid='" +
        fieldDescriptor.fieldId +
        "']");
    return fieldElement;
}
export function findComponentRect(iframe, component, takeScrollPositionIntoAccount = false) {
    const body = getAccessibleIframeDocument(iframe)?.body;
    if (!body)
        return undefined;
    // Query elements from the passed iframe document to ensure we get elements from the correct document
    // This is important for minimap where we need elements from the minimap's iframe, not the main view
    const componentStartMarker = body.querySelector("[data-component-start='" + component.id + "']");
    const componentEndMarker = body.querySelector("[data-component-end='" + component.id + "']");
    // Try to get element from passed iframe first, fall back to stored reference
    let startElement = componentStartMarker?.nextElementSibling;
    while (startElement &&
        startElement !== componentEndMarker &&
        startElement.tagName === "SCRIPT") {
        startElement = startElement.nextElementSibling;
    }
    // Marker-only/meta components can have adjacent start/end markers with no
    // measurable DOM between them. Treat them as non-visual instead of logging.
    if (startElement === componentEndMarker) {
        return null;
    }
    if (!startElement) {
        startElement = component.firstDOMElement;
        while (startElement && startElement.tagName === "SCRIPT") {
            startElement = startElement.nextElementSibling;
        }
    }
    if (!startElement) {
        console.error("No start element found for component " + component.id);
        return null;
    }
    const startRect = takeScrollPositionIntoAccount
        ? getAbsolutePosition(startElement, iframe)
        : startElement.getBoundingClientRect();
    let endElement = componentEndMarker?.previousElementSibling || component.lastDOMElement;
    while (endElement && endElement.tagName === "SCRIPT") {
        endElement = endElement.previousElementSibling;
    }
    if (!endElement)
        return null;
    const endRect = takeScrollPositionIntoAccount
        ? getAbsolutePosition(endElement, iframe)
        : endElement.getBoundingClientRect();
    const elements = startElement === endElement ? [startElement] : [startElement, endElement];
    return {
        rect: {
            x: startRect.x,
            y: startRect.y,
            width: endRect.x + endRect.width - startRect.x,
            height: endRect.y + endRect.height - startRect.y,
        },
        elements: elements,
    };
}
// export function findComponentRect(
//   iframe: HTMLIFrameElement,
//   componentId: string,
//   takeScrollPositionIntoAccount = false,
// ) {
//   if (!iframe.contentWindow?.document.body) return undefined;
//   const componentStart = iframe.contentWindow?.document.body.querySelector(
//     "[data-component-start='" + componentId + "']",
//   );
//   const componentEnd = iframe?.contentWindow?.document.body.querySelector(
//     "[data-component-end='" + componentId + "']",
//   );
//   if (!componentStart || !componentEnd) {
//     return null;
//   }
//   let startElement = componentStart.nextElementSibling;
//   while (startElement && startElement.tagName === "SCRIPT") {
//     startElement = startElement.nextElementSibling;
//   }
//   if (!startElement) {
//     console.error("No start element found for component " + componentId);
//     return null;
//   }
//   const startRect = takeScrollPositionIntoAccount
//     ? getAbsolutePosition(startElement as HTMLElement, iframe)
//     : startElement.getBoundingClientRect();
//   let endElement = componentEnd.previousElementSibling;
//   while (endElement && endElement.tagName === "SCRIPT") {
//     endElement = endElement.previousElementSibling;
//   }
//   if (!endElement) return null;
//   const endRect = takeScrollPositionIntoAccount
//     ? getAbsolutePosition(endElement as HTMLElement, iframe)
//     : endElement.getBoundingClientRect();
//   const elements =
//     startElement === endElement ? [startElement] : [startElement, endElement];
//   // console.log(
//   //   "start rect",
//   //   startRect,
//   //   startElement,
//   //   iframe.contentWindow?.scrollY,
//   //   takeScrollPositionIntoAccount
//   // );
//   return {
//     rect: {
//       x: startRect.x,
//       y: startRect.y,
//       width: endRect.x + endRect.width - startRect.x,
//       height: endRect.y + endRect.height - startRect.y,
//     },
//     elements: elements,
//   };
// }
export const getAbsolutePosition = (element, iframe) => {
    const rect = element.getBoundingClientRect();
    let scrollLeft = 0;
    let scrollTop = 0;
    try {
        scrollLeft =
            iframe.contentWindow?.scrollX ||
                iframe.contentDocument?.documentElement.scrollLeft ||
                0;
        scrollTop =
            iframe.contentWindow?.scrollY ||
                iframe.contentDocument?.documentElement.scrollTop ||
                0;
    }
    catch {
        scrollLeft = 0;
        scrollTop = 0;
    }
    return {
        x: rect.left + scrollLeft,
        y: rect.top + scrollTop,
        width: rect.width,
        height: rect.height,
    };
};
// export function findParentComponentId(element: HTMLElement) {
//   while (element && element !== document.documentElement) {
//     if (element.getAttribute("sc_item")) {
//       return extractItemIdFromItemUri(element.getAttribute("sc_item")!);
//     }
//     const startElement = findStartElement(element);
//     if (startElement) {
//       return startElement.getAttribute("data-component-start");
//     }
//     element = element.parentElement as HTMLElement;
//   }
//   return null;
// }
// function findStartElement(element: HTMLElement) {
//   let prev: Element | null = element;
//   while (prev) {
//     prev = prev.previousElementSibling;
//     if (
//       prev?.getAttribute("data-component-start") &&
//       !prev.hasAttribute("data-skip-component")
//     ) {
//       return prev;
//     }
//   }
//   return null;
// }
export function stripGuid(id) {
    return id.replace(/[{}]/g, "").toLowerCase();
}
export function normalizeGuid(id) {
    id = id.toUpperCase();
    if (id.length && id[0] === "{")
        return id;
    return "{" + id + "}";
}
function getLocale() {
    try {
        if (typeof navigator !== "undefined" && navigator.language)
            return navigator.language;
    }
    catch { }
    return "en";
}
function getUserTimeZone() {
    try {
        const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
        return tz || "UTC";
    }
    catch {
        return "UTC";
    }
}
export function formatDate(date, options) {
    const locale = getLocale();
    const timeZone = options?.timeZone || getUserTimeZone();
    const fmt = new Intl.DateTimeFormat(locale, {
        year: "numeric",
        month: "numeric",
        day: "numeric",
        hour: "numeric",
        minute: "numeric",
        second: "numeric",
        hour12: false,
        timeZone,
        ...options,
    });
    return fmt.format(date);
}
export function formatDateOnly(date, options) {
    const locale = getLocale();
    const timeZone = options?.timeZone || getUserTimeZone();
    const fmt = new Intl.DateTimeFormat(locale, {
        year: "numeric",
        month: "numeric",
        day: "numeric",
        timeZone,
        ...options,
    });
    return fmt.format(date);
}
export function formatTime(date, options) {
    const locale = getLocale();
    const timeZone = options?.timeZone || getUserTimeZone();
    const fmt = new Intl.DateTimeFormat(locale, {
        hour: "numeric",
        minute: "numeric",
        second: "numeric",
        hour12: false,
        timeZone,
        ...options,
    });
    return fmt.format(date);
}
export function formatDateTime(date, options) {
    return formatDate(date, options);
}
export function findClosestFieldElement(node) {
    let current = node;
    while (current) {
        if (current.nodeType === Node.ELEMENT_NODE) {
            const element = current;
            if (element.hasAttribute("data-fieldid")) {
                return element;
            }
        }
        current = current.parentNode;
    }
    return null;
}
// GUID helpers for clipboard parsing and validation
export const GUID_REGEX_GLOBAL = /[\{\(]?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}[\}\)]?/g;
export const GUID_REGEX_EXACT = /^[\{\(]?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}[\}\)]?$/;
// Non-global regex for quick containment checks (avoids lastIndex issues with .test on /g)
export const GUID_REGEX_CONTAINS = /[\{\(]?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}[\}\)]?/;
export function containsGuidInText(text) {
    if (!text)
        return false;
    return GUID_REGEX_CONTAINS.test(text);
}
export function findGuidsInText(text) {
    if (!text)
        return [];
    const matches = text.match(GUID_REGEX_GLOBAL) || [];
    const normalized = matches
        .map((m) => m.replace(/[{}()]/g, ""))
        .filter((m) => !!m)
        .map((m) => normalizeGuid(m));
    // Deduplicate while preserving order
    const seen = new Set();
    const unique = [];
    for (const id of normalized) {
        if (!seen.has(id)) {
            seen.add(id);
            unique.push(id);
        }
    }
    return unique;
}
//# sourceMappingURL=utils.js.map