import { findParentElement } from "./dom";
import { createLogger, PlatformServiceName, } from "@limetech/lime-web-components";
const logger = createLogger('object-resolver');
/**
 * Build the navigation URL for a Lime CRM object.
 * @param limetype
 * @param objectid
 */
export function getObjectUrl(limetype, objectid) {
    return `object/${limetype}/${objectid}`;
}
/**
 * Resolves platform, limetype metadata, and object data for a given
 * limetype + objectid pair.
 *
 * Designed to be used by Stencil components: pass an `onStateChange`
 * callback that updates a `@State()` property to trigger re-renders.
 */
export class ObjectResolver {
    constructor(onStateChange) {
        this.onStateChange = onStateChange;
        this.state = { loadFailed: false };
        this.hasPendingLoad = false;
        this.isDisconnected = false;
    }
    /**
     * The resolved platform instance, if any.
     */
    get resolvedPlatform() {
        return this._platform;
    }
    /**
     * Initialize the resolver. Call from `connectedCallback()`.
     *
     * @param limetype - name of the limetype
     * @param objectid - id of the object
     * @param hostElement - the component's host element (for DOM-walking fallback)
     * @param platform - optional platform prop (skips DOM walk if provided)
     */
    connect(limetype, objectid, hostElement, platform) {
        this.limetype = limetype;
        this.objectid = objectid;
        this.isDisconnected = false;
        this.hasPendingLoad = false;
        this._platform = platform || this.findPlatform(hostElement);
        if (!this._platform) {
            logger.error(`No platform found. Pass "platform" prop or place the component inside a parent that exposes "platform". limetype=${limetype}, objectid=${objectid}`);
            return;
        }
        this.resolveLimeType();
        this.loadLimeObject();
    }
    /**
     * Handle prop changes. Call from `@Watch` handlers.
     *
     * Debounces via `queueMicrotask` so that simultaneous changes to
     * both `limetype` and `objectid` result in a single load.
     * @param limetype
     * @param objectid
     */
    update(limetype, objectid) {
        this.limetype = limetype;
        this.objectid = objectid;
        this.updateState({
            limeobject: undefined,
            limetypeData: undefined,
            loadFailed: false,
        });
        if (this.hasPendingLoad) {
            return;
        }
        this.hasPendingLoad = true;
        queueMicrotask(() => {
            this.hasPendingLoad = false;
            if (this.isDisconnected) {
                return;
            }
            if (!this._platform) {
                this.updateState({ loadFailed: true });
                logger.warn(`Cannot update: no platform available. limetype=${this.limetype}, objectid=${this.objectid}`);
                return;
            }
            this.resolveLimeType();
            this.loadLimeObject();
        });
    }
    /**
     * Prevent stale updates after the component disconnects.
     * Call from `disconnectedCallback()`.
     */
    disconnect() {
        this.isDisconnected = true;
    }
    findPlatform(hostElement) {
        const element = findParentElement(hostElement, this.hasPlatformProp);
        return element === null || element === void 0 ? void 0 : element.platform;
    }
    hasPlatformProp(element) {
        if (!('platform' in element)) {
            return false;
        }
        if (!element.platform) {
            return false;
        }
        return typeof element.platform === 'object';
    }
    resolveLimeType() {
        const repository = this._platform.get(PlatformServiceName.LimeTypeRepository);
        this.updateState({
            limetypeData: repository.getLimeType(this.limetype),
        });
    }
    async loadLimeObject() {
        const limetype = this.limetype;
        const objectid = this.objectid;
        const repository = this._platform.get(PlatformServiceName.LimeObjectRepository);
        const cached = repository.getObject(limetype, objectid);
        if (cached) {
            this.updateState({ limeobject: cached });
            return;
        }
        try {
            await repository.loadObject(limetype, objectid);
        }
        catch (error) {
            if (this.isStale(limetype, objectid)) {
                return;
            }
            this.updateState({ loadFailed: true });
            logger.error(`Failed to load ${limetype}/${objectid}`, error);
            return;
        }
        if (this.isStale(limetype, objectid)) {
            return;
        }
        this.updateState({
            limeobject: repository.getObject(limetype, objectid),
        });
    }
    isStale(limetype, objectid) {
        return (this.isDisconnected ||
            this.limetype !== limetype ||
            this.objectid !== objectid);
    }
    updateState(partial) {
        this.state = Object.assign(Object.assign({}, this.state), partial);
        if (!this.isDisconnected) {
            this.onStateChange(this.state);
        }
    }
}
