import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { StorageClient } from '@atlaskit/frontend-utilities/storage-client';
import { getCurrentSiteCloudId, getCachedCurrentSiteCloudIdAndRefresh } from '../current-site-cloud-id';
const BASE_URL = '/gateway/api/tap-delivery/api/v3/personalization';

/**
 * Logical key shape: `@atlaskit/smart-card:<feature>:<schema-version>:<scope>` (see smart-card
 * storage conventions). {@link StorageClient} narrows the localStorage key to
 * `<clientKey>_<itemKey>` with `clientKey === '@atlaskit/smart-card'` and
 * `itemKey === 'pct-map:v1:<cloudId>:<traitName>'` (scope segments URI-encoded).
 */
export const PERSONALIZATION_STORAGE_SCOPE = '@atlaskit/smart-card';
export const PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX = 'pct-map:v1:';
const smartCardStorage = new StorageClient(PERSONALIZATION_STORAGE_SCOPE);

/** Keys written by this service in localStorage when using {@link smartCardStorage}. */
const LOCAL_STORAGE_ROW_KEY_PREFIX = `${PERSONALIZATION_STORAGE_SCOPE}_${PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX}`;
function pctMapStorageItemKey(cloudId, traitName) {
  return `${PERSONALIZATION_STORAGE_ITEM_KEY_PREFIX}${encodeURIComponent(cloudId)}:${encodeURIComponent(traitName)}`;
}
async function fetchSiteTraits(cloudId) {
  var _data$attributes;
  const response = await fetch(`${BASE_URL}/site/${cloudId}`, {
    method: 'GET',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json'
    }
  });
  if (!response.ok) {
    return [];
  }
  const data = await response.json();
  return (_data$attributes = data === null || data === void 0 ? void 0 : data.attributes) !== null && _data$attributes !== void 0 ? _data$attributes : [];
}

/**
 * Service for fetching site-level traits from the TAP Delivery personalization API.
 */
export class PersonalizationService {
  constructor() {
    _defineProperty(this, "cache", new Map());
  }
  /**
   * Returns the currently cached provider percentage map synchronously and starts a background refresh.
   * The refresh result is persisted for future calls but is not awaited by this call.
   */
  getCachedProviderPctMapAndRefresh(traitName) {
    const cloudId = getCachedCurrentSiteCloudIdAndRefresh();
    const fromStorage = cloudId && this.readStoredProviderPctMap(cloudId, traitName) || null;
    void this.getProviderPctMap(traitName);
    return fromStorage;
  }
  async getProviderPctMap(traitName) {
    const cachedPromise = this.cache.get(traitName);
    if (cachedPromise) {
      return cachedPromise;
    }
    const promise = (async () => {
      try {
        const cloudId = await getCurrentSiteCloudId();
        if (!cloudId) {
          return undefined;
        }
        const traits = await fetchSiteTraits(cloudId);
        const trait = traits.find(t => t.name === traitName);
        const mapped = this.parseTraitValue(trait === null || trait === void 0 ? void 0 : trait.value);
        if (mapped !== undefined) {
          this.writeStoredProviderPctMap(cloudId, traitName, mapped);
        }
        return mapped;
      } catch {
        return undefined;
      }
    })();
    this.cache.set(traitName, promise);
    return promise;
  }
  readStoredProviderPctMap(cloudId, traitName) {
    try {
      const stored = smartCardStorage.getItem(pctMapStorageItemKey(cloudId, traitName));
      if (stored === undefined || stored === null) {
        return null;
      }
      return this.normalizeProviderPctMap(stored);
    } catch {
      return null;
    }
  }
  writeStoredProviderPctMap(cloudId, traitName, map) {
    try {
      smartCardStorage.setItemWithExpiry(pctMapStorageItemKey(cloudId, traitName), map);
    } catch {
      // Quota, private-mode, etc.
    }
  }
  normalizeProviderPctMap(value) {
    if (typeof value !== 'object' || value === null || Array.isArray(value)) {
      return null;
    }
    const map = {};
    const entries = Object.entries(value);
    for (const [providerKey, percentageRaw] of entries) {
      if (typeof percentageRaw !== 'number' || !Number.isFinite(percentageRaw)) {
        return null;
      }
      map[providerKey] = percentageRaw;
    }
    return map;
  }
  parseTraitValue(raw) {
    if (typeof raw !== 'string') {
      return undefined;
    }
    try {
      const parsed = JSON.parse(raw);
      const normalized = this.normalizeProviderPctMap(parsed);
      return normalized === null ? undefined : normalized;
    } catch {
      return undefined;
    }
  }
  clearCache() {
    this.cache.clear();
    this.clearStoredProviderPctMaps();
  }
  clearStoredProviderPctMaps() {
    if (typeof globalThis.localStorage === 'undefined' || globalThis.localStorage === null) {
      return;
    }
    const keysToRemove = [];
    for (let index = 0; index < globalThis.localStorage.length; index += 1) {
      const key = globalThis.localStorage.key(index);
      if (key !== null && key.startsWith(LOCAL_STORAGE_ROW_KEY_PREFIX)) {
        keysToRemove.push(key);
      }
    }
    for (const key of keysToRemove) {
      try {
        globalThis.localStorage.removeItem(key);
      } catch {
        /* ignore */
      }
    }
  }
}
export const personalizationService = new PersonalizationService();

/**
 * Resolves the provider percentage map for a TAP Delivery trait through the module-level
 * {@link personalizationService}. Work is deduped per trait name for the page lifetime, and a
 * successful response is persisted by cloud id and trait name for later cached reads.
 */
export const getProviderPctMap = traitName => personalizationService.getProviderPctMap(traitName);

/**
 * Reads the provider percentage map for a trait from browser storage via the module-level
 * {@link personalizationService} singleton, without awaiting network work.
 * Calling this also starts the trait-scoped shared refresh in the background, so a later call can
 * use a refreshed value when it becomes available.
 */
export function getCachedProviderPctMapAndRefresh(traitName) {
  return personalizationService.getCachedProviderPctMapAndRefresh(traitName);
}