/*
 * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import deepEqual from "fast-deep-equal";
import { AbstractHelper } from "../AbstractHelper.js";
import { tombstone } from "../tombstone.js";
import { mergeObjectFields } from "../utils/rdpFieldOperations.js";
import { ObjectQuery } from "./ObjectQuery.js";
export class ObjectsHelper extends AbstractHelper {
  observe(options, subFn) {
    return super.observe(options, subFn);
  }
  getQuery(options, rdpConfig) {
    const apiName = typeof options.apiName === "string" ? options.apiName : options.apiName.apiName;
    const {
      pk
    } = options;
    const objectCacheKey = this.cacheKeys.get("object", apiName, pk, rdpConfig ?? undefined);
    return this.store.queries.get(objectCacheKey, () => new ObjectQuery(this.store, this.store.subjects.get(objectCacheKey), apiName, pk, objectCacheKey, {
      dedupeInterval: 0
    }));
  }

  /**
   * Internal helper method for writing objects to the store and returning their
   * object keys. For list queries with RDPs, the rdpConfig is included in the
   * cache key to ensure proper data isolation.
   * @internal
   */
  storeOsdkInstances(values, batch, rdpConfig) {
    // update the cache for any object that has changed
    // and save the mapped values to return
    return values.map(v => this.getQuery({
      apiName: v.$apiName,
      pk: v.$primaryKey
    }, rdpConfig).writeToStore(v, "loaded", batch).cacheKey);
  }

  /**
   * Write an object to cache and propagate to all related cache keys
   * @internal
   */
  propagateWrite(sourceCacheKey, value, status, batch) {
    const existing = batch.read(sourceCacheKey);
    const dataChanged = !existing || existing.value === undefined || value === tombstone || !deepEqual(existing.value, value);
    const statusChanged = !existing || existing.status !== status;
    if (!dataChanged && !statusChanged) {
      return;
    }
    const valueToWrite = !dataChanged && existing ? existing.value : value;
    batch.write(sourceCacheKey, valueToWrite, status);
    if (value !== tombstone) {
      batch.changes.registerObject(sourceCacheKey, value, !existing);
    }
    const metadata = this.store.objectCacheKeyRegistry.getMetadata(sourceCacheKey);
    const relatedKeys = metadata ? this.store.objectCacheKeyRegistry.getVariants(metadata.apiName, metadata.primaryKey) : new Set([sourceCacheKey]);
    for (const targetKey of relatedKeys) {
      if (targetKey === sourceCacheKey || !this.isKeyActive(targetKey)) {
        continue;
      }
      if (value === tombstone) {
        batch.write(targetKey, tombstone, status);
        continue;
      }
      const targetCurrentValue = batch.read(targetKey)?.value;
      const merged = this.mergeForTarget(value, targetCurrentValue && this.isObjectHolder(targetCurrentValue) ? targetCurrentValue : undefined, sourceCacheKey, targetKey);
      batch.write(targetKey, merged, status);
    }
  }

  /**
   * Check if a cache key is actively observed
   */
  isKeyActive(key) {
    const subject = this.store.subjects.peek(key);
    return subject?.observed === true;
  }

  /**
   * Type guard to check if a value is an ObjectHolder
   */
  isObjectHolder(value) {
    return value != null && typeof value === "object" && "$apiName" in value && "$primaryKey" in value;
  }

  /**
   * Merge object data for a specific target cache key, preserving RDP fields
   */
  mergeForTarget(sourceValue, targetCurrentValue, sourceCacheKey, targetCacheKey) {
    const sourceRdpFields = this.store.objectCacheKeyRegistry.getRdpFieldSet(sourceCacheKey);
    const targetRdpFields = this.store.objectCacheKeyRegistry.getRdpFieldSet(targetCacheKey);
    return mergeObjectFields(sourceValue, sourceRdpFields, targetRdpFields, targetCurrentValue);
  }
}
//# sourceMappingURL=ObjectsHelper.js.map