import AsyncStorage from '@react-native-async-storage/async-storage';
import { Client as ASCClient, AdRepository } from '@amityco/ts-sdk-react-native';
import { TimeWindowTracker } from './TimeWindowTracker';
import AdAssetCache from './AdAssetCache';
import AssetDownloader, { DownloadStatus } from './AssetDownloader';
import { AdSupplier } from './AdSupplier';
class SeenRecencyCache {
  static #instance;
  #persistentCacheKey = 'amity.seenRecencyCache';
  constructor() {}
  static get instance() {
    if (!SeenRecencyCache.#instance) {
      SeenRecencyCache.#instance = new SeenRecencyCache();
    }
    return SeenRecencyCache.#instance;
  }
  async getSeenRecencyCache() {
    return JSON.parse((await AsyncStorage.getItem(this.#persistentCacheKey)) || '{}');
  }
  getSeenRecencyByAdId(adId) {
    if (!adId) return;
    return this.getSeenRecencyCache()[adId];
  }
  async setSeenRecencyCache(adId, value) {
    const seenRecencyCache = this.getSeenRecencyCache();
    seenRecencyCache[adId] = value;
    await AsyncStorage.setItem(this.#persistentCacheKey, JSON.stringify(seenRecencyCache));
  }
  async clear() {
    await AsyncStorage.removeItem(this.#persistentCacheKey);
  }
}
export class AdEngine {
  static #instance;
  isLoading = true;
  ads = [];
  settings = null;
  subscribers = [];
  constructor() {
    ASCClient.onSessionStateChange(async state => {
      if (state === 'established') {
        await this.consumeNetworkAds();
      } else if (state === 'terminated') {
        this.clearAllCache();
      }
    });
  }
  static get instance() {
    if (!AdEngine.#instance) {
      AdEngine.#instance = new AdEngine();
    }
    return AdEngine.#instance;
  }
  onNetworkAdsData(callback) {
    if (!this.isLoading && this.ads.length > 0 && this.settings) {
      callback({
        ads: this.ads,
        settings: this.settings
      });
    }
    this.subscribers.push(callback);
  }
  async clearAllCache() {
    this.ads = [];
    this.settings = null;
    const allAssets = await AdAssetCache.instance.getAllAdAssets();
    for (const asset of allAssets) {
      AssetDownloader.instance.deleteFile(asset.fileUrl);
    }
    AdAssetCache.instance.deleteAll();
    SeenRecencyCache.instance.clear();
    TimeWindowTracker.instance.clear();
    this.subscribers.forEach(subscriber => subscriber(null));
  }
  async consumeNetworkAds() {
    const networkAds = await AdRepository.getNetworkAds();
    this.ads = networkAds.ads;
    this.settings = networkAds.settings;
    await this.diffAssets(this.generateAssets(this.ads));
    await this.refreshDownloadStatuses();
    this.subscribers.forEach(subscriber => subscriber(networkAds));
  }
  generateAssets(ads) {
    const assets = ads.flatMap(ad => {
      var _ad$image1_, _ad$image9_;
      return [ad === null || ad === void 0 || (_ad$image1_ = ad.image1_1) === null || _ad$image1_ === void 0 ? void 0 : _ad$image1_.fileUrl, ad === null || ad === void 0 || (_ad$image9_ = ad.image9_16) === null || _ad$image9_ === void 0 ? void 0 : _ad$image9_.fileUrl].filter(Boolean);
    }).map(url => ({
      fileUrl: url + '?size=large',
      downloadStatus: -1,
      downloadId: null
    }));
    return assets;
  }
  async diffAssets(newAsset) {
    const oldAssets = await AdAssetCache.instance.getAllAdAssets();
    const newAssetUrls = newAsset.map(asset => asset.fileUrl);
    const assetToInsert = newAsset.filter(asset => !oldAssets.some(oldAsset => oldAsset.fileUrl === asset.fileUrl));
    const assetToDelete = oldAssets.filter(oldAsset => !newAssetUrls.includes(oldAsset.fileUrl));
    assetToDelete.forEach(asset => {
      AdAssetCache.instance.deleteAdAsset(asset.fileUrl);
    });
    assetToInsert.forEach(asset => {
      AdAssetCache.instance.insertAdAsset(asset);
    });
  }
  async refreshDownloadStatuses() {
    const assets = await AdAssetCache.instance.getAllAdAssets();
    for (const asset of assets) {
      const fileExists = await AssetDownloader.instance.fileExists(asset.fileUrl);
      if (asset.downloadStatus === DownloadStatus.NOT_DOWNLOADED || asset.downloadStatus === DownloadStatus.FAILED || asset.downloadStatus === DownloadStatus.COMPLETED && !fileExists) {
        try {
          const downloadId = await AssetDownloader.instance.enqueue(asset.fileUrl);
          AdAssetCache.instance.updateDownloadId(asset.fileUrl, downloadId);
          AdAssetCache.instance.updateDownloadStatus(asset.fileUrl, DownloadStatus.DOWNLOADING);

          // Listen to download status, to update the status when it changes
          AssetDownloader.instance.addStatusListener(asset.fileUrl, status => {
            AdAssetCache.instance.updateDownloadStatus(asset.fileUrl, status);
          });
        } catch (e) {
          console.log('error: 3', e);
        }
      } else if (asset.downloadStatus !== DownloadStatus.COMPLETED) {
        AdAssetCache.instance.updateDownloadStatus(asset.fileUrl, AssetDownloader.instance.getDownloadStatus(asset.downloadId));
      }
    }
  }
  getAdFrequency(placement) {
    if (!this.settings) return null;
    switch (placement) {
      case 'feed':
        return this.settings.frequency.feed;
      case 'comment':
        return this.settings.frequency.comment;
      case 'story':
        return this.settings.frequency.story;
      default:
        return null;
    }
  }
  getUrlByPlacement(ad, placement) {
    var _ad$image9_2, _ad$image1_2;
    if (!this.settings) return null;
    const suffix = '?size=large';
    if (placement === 'story') return ((_ad$image9_2 = ad.image9_16) === null || _ad$image9_2 === void 0 ? void 0 : _ad$image9_2.fileUrl) + suffix;
    return ((_ad$image1_2 = ad.image1_1) === null || _ad$image1_2 === void 0 ? void 0 : _ad$image1_2.fileUrl) + suffix;
  }
  getLastSeen(adId) {
    return SeenRecencyCache.instance.getSeenRecencyByAdId(adId);
  }
  markClicked(ad, placement) {
    ad.analytics.markLinkAsClicked(placement);
  }
  markSeen(ad, placement) {
    var _this$getAdFrequency;
    // update recency seen time as now
    SeenRecencyCache.instance.setSeenRecencyCache(ad.adId, Date.now());
    if (((_this$getAdFrequency = this.getAdFrequency(placement)) === null || _this$getAdFrequency === void 0 ? void 0 : _this$getAdFrequency.type) === 'time-window') {
      TimeWindowTracker.instance.markSeen(placement);
    }
    ad.analytics.markAsSeen(placement);
  }
  getAdFrequencyByPlacement(placement) {
    return this.getAdFrequency(placement);
  }
  async getRecommendedAds({
    placement,
    count,
    communityId
  }) {
    const applicableAds = await this.getApplicableAds(placement, communityId);
    if (applicableAds.length === 0) {
      return [];
    } else {
      return AdSupplier.instance.recommendedAds({
        ads: applicableAds,
        count,
        placement,
        communityId
      });
    }
  }
  async getApplicableAds(placement, communityId) {
    var _this$settings;
    if (!((_this$settings = this.settings) !== null && _this$settings !== void 0 && _this$settings.enabled) || this.getAdFrequencyByPlacement(placement).type === 'time-window' && TimeWindowTracker.instance.hasReachedLimit(placement)) return [];

    // Step 1: Map each ad to a promise that resolves to {ad, isReady}
    const readyAdsPromises = await Promise.all(this.ads.map(async ad => {
      var _ad$advertiser;
      const url = this.getUrlByPlacement(ad, placement);
      const isReady = ad.placements.includes(placement) && (!ad.endAt || new Date(ad.endAt).getTime() > Date.now()) && url && (await AdAssetCache.instance.getAdAsset(url)).downloadStatus === DownloadStatus.COMPLETED && ((_ad$advertiser = ad.advertiser) === null || _ad$advertiser === void 0 ? void 0 : _ad$advertiser.advertiserId) !== communityId;
      return {
        ad,
        isReady
      };
    }));

    // Step 2: Filter and extract the ads
    const readyAds = readyAdsPromises.filter(({
      isReady
    }) => isReady).map(({
      ad
    }) => ad);

    // Handle targeting logic
    let applicableAds;
    if (communityId) {
      // First try to find ads targeted at this community
      const targetedAds = readyAds.filter(ad => {
        var _ad$target;
        return (_ad$target = ad.target) === null || _ad$target === void 0 || (_ad$target = _ad$target.communityIds) === null || _ad$target === void 0 ? void 0 : _ad$target.includes(communityId);
      });

      // If none found, fall back to non-targeted ads
      applicableAds = targetedAds.length > 0 ? targetedAds : readyAds.filter(ad => {
        var _ad$target2;
        return ((_ad$target2 = ad.target) === null || _ad$target2 === void 0 || (_ad$target2 = _ad$target2.communityIds) === null || _ad$target2 === void 0 ? void 0 : _ad$target2.length) === 0;
      });
    } else {
      // No community ID provided, only use non-targeted ads

      applicableAds = readyAds.filter(ad => {
        var _ad$target3;
        return !ad.target || ((_ad$target3 = ad.target) === null || _ad$target3 === void 0 ? void 0 : _ad$target3.communityIds.length) === 0;
      });
    }
    return applicableAds;
  }
}
export default AdEngine;
//# sourceMappingURL=AdEngine.js.map