import { initIdentity } from '../utils/identity.utils';
import { uploadDeckBackgroundAssets, uploadParagraphImages, uploadSlideAssets } from '../utils/sync.storage.utils';
import { uploadDeckData, uploadDocData, uploadParagraphData, uploadSlideData } from '../utils/sync.data.utils';
import { updateDeckBackground, updateParagraphImages, updateSlideChart, updateSlideImages } from '../utils/sync.attributes.utils';
import { getDataBucket, getStorageBucket } from '../utils/manager.utils';
import { deleteData } from '../utils/data.utils';
export const uploadWorker = async ({ internetIdentity: { delegationChain, identityKey }, syncData, host }, syncWindow) => {
  if (!syncData) {
    return;
  }
  if (!delegationChain || !identityKey) {
    return;
  }
  const identity = initIdentity({ identityKey, delegationChain });
  const { updateDecks, updateDocs, updateSlides, updateParagraphs, deleteSlides: slidesToDelete, deleteParagraphs: paragraphsToDelete } = syncData;
  const promises = [
    getDataBucket({ host, identity }),
    getStorageBucket({ host, identity })
  ];
  const [dataBucket, storageBucket] = await Promise.all(promises);
  // For performance reason, we query the actor only once. We might not need it but, most often we will and multiple times.
  const { actor } = dataBucket;
  // In case the bucket does not yet exist, do not sync yet and wait for next poll
  // Can most probably not happens since user auth create such bucket and triggers the sync when ready
  if (!actor) {
    return;
  }
  // TODO: what's the impact in term of performance to query it every time? indeed we need to init it the very first time to initialize the bucket but, we might not need it to sync data most of the time?
  // We query the storage actor until it is initialized and use it to upload the assets
  const { actor: storageActor } = storageBucket;
  if (!storageActor) {
    return;
  }
  await uploadDecks({ updateDecks, identity, dataActor: actor, storageBucket, syncWindow });
  await uploadSlides({ updateSlides, identity, actor, storageBucket, syncWindow });
  await deleteSlides({ deleteSlides: slidesToDelete, actor });
  await uploadDocs({ updateDocs, actor });
  await uploadParagraphs({ updateParagraphs, identity, actor, storageBucket, syncWindow });
  await deleteParagraphs({ deleteParagraphs: paragraphsToDelete, actor });
};
const uploadDecks = async ({ updateDecks, identity, dataActor, storageBucket, syncWindow }) => {
  if (!updateDecks || updateDecks.length <= 0) {
    return;
  }
  const promises = updateDecks.map(({ deck }) => uploadDeck({ deck, dataActor, storageBucket, identity, syncWindow }));
  await Promise.all(promises);
  console.log('Deck IC synced');
};
const uploadDeck = async ({ deck, dataActor, storageBucket, identity, syncWindow }) => {
  if (!deck) {
    return;
  }
  // 1. We upload the asset to the IC (worker side), update DOM and IDB (window side for thread safe reason) and clean the asset from IDB
  const { src: imgSrc, storageFile } = await uploadDeckBackgroundAssets({ deck, identity, syncWindow, storageBucket });
  // 2. If we uploaded an asset, its URL has changed (no more local but available online)
  const updateDeck = updateDeckBackground({ deck, imgSrc, storageFile });
  // 3. We can update the data in the IC
  await uploadDeckData({ deck: updateDeck, actor: dataActor });
};
const uploadSlides = async ({ updateSlides, identity, actor, syncWindow, storageBucket }) => {
  if (!updateSlides || updateSlides.length <= 0) {
    return;
  }
  const promises = updateSlides.map(({ slide, deckId }) => uploadSlide({ slide, deckId, actor, identity, storageBucket, syncWindow }));
  await Promise.all(promises);
};
const deleteSlides = async ({ deleteSlides, actor }) => {
  if (!deleteSlides || deleteSlides.length <= 0) {
    return;
  }
  const promises = deleteSlides
    .filter(({ slideId }) => slideId !== undefined)
    .map(({ deckId, slideId }) => deleteData({ key: `/decks/${deckId}/slides/${slideId}`, actor }));
  await Promise.all(promises);
};
const uploadSlide = async ({ slide, deckId, actor, identity, syncWindow, storageBucket }) => {
  if (!slide) {
    return;
  }
  // 1. We upload the asset to the IC (worker side), update DOM and IDB (window side for thread safe reason) and clean the asset from IDB
  const { images, chart } = await uploadSlideAssets({ slide, deckId, storageBucket, identity, syncWindow });
  // 2. If we uploaded assets, there URL have changed (no more local but available online)
  const updateChartSlide = updateSlideChart({ slide, chart });
  const updateSlide = updateSlideImages({ slide: updateChartSlide, images });
  // 3. We can update the data in the IC
  await uploadSlideData({ deckId, slide: updateSlide, actor });
  console.log(`Slide (${updateSlide.id}) IC synced`);
};
const uploadDocs = async ({ updateDocs, actor }) => {
  if (!updateDocs || updateDocs.length <= 0) {
    return;
  }
  const promises = updateDocs.map(({ doc }) => uploadDoc({ doc, actor }));
  await Promise.all(promises);
  console.log('Doc IC synced');
};
const uploadDoc = async ({ doc, actor }) => {
  if (!doc) {
    return;
  }
  await uploadDocData({ doc, actor });
};
const uploadParagraphs = async ({ updateParagraphs, identity, actor, syncWindow, storageBucket }) => {
  if (!updateParagraphs || updateParagraphs.length <= 0) {
    return;
  }
  const promises = updateParagraphs.map(({ paragraph, docId }) => uploadParagraph({ paragraph, docId, actor, identity, storageBucket, syncWindow }));
  await Promise.all(promises);
};
const uploadParagraph = async ({ paragraph, docId, actor, identity, syncWindow, storageBucket }) => {
  if (!paragraph) {
    return;
  }
  // 1. We upload the asset to the IC (worker side), update DOM and IDB (window side for thread safe reason) and clean the asset from IDB
  const images = await uploadParagraphImages({ paragraph, docId, storageBucket, identity, syncWindow });
  // 2. If we uploaded assets, there URL have changed (no more local but available online)
  const updateParagraph = updateParagraphImages({ paragraph: paragraph, images });
  // 3. We can update the data in the IC
  await uploadParagraphData({ docId, paragraph: updateParagraph, actor });
  console.log(`Paragraph (${updateParagraph.id}) IC synced`);
};
const deleteParagraphs = async ({ deleteParagraphs, actor }) => {
  if (!deleteParagraphs || deleteParagraphs.length <= 0) {
    return;
  }
  const promises = deleteParagraphs
    .filter(({ paragraphId }) => paragraphId !== undefined)
    .map(({ docId, paragraphId }) => deleteData({ key: `/docs/${docId}/paragraphs/${paragraphId}`, actor }));
  await Promise.all(promises);
};
