var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function (resolve) {
      resolve(value);
    });
  }

  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }

    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }

    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }

    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};

import { keccak256Hash } from "../utils/hash.js";
import { serializeBytes } from "../chunk/serialize.js";
import { fetchLatestFeedUpdate } from "../modules/feed.js";
import { makeSingleOwnerChunkFromData, uploadSingleOwnerChunkData } from "../chunk/soc.js";
import { FEED_INDEX_HEX_LENGTH } from "../types/index.js";
import { bytesAtOffset, makeBytes } from "../utils/bytes.js";
import { BeeResponseError } from "../utils/error.js";
import { bytesToHex, hexToBytes, makeHexString } from "../utils/hex.js";
import { readUint64BigEndian, writeUint64BigEndian } from "../utils/uint64.js";
import * as chunkAPI from "../modules/chunk.js";
import { makeHexEthAddress } from "../utils/eth.js";
import { assertAddress } from "../utils/type.js";
import { makeFeedIdentifier } from "./identifier.js";
import { makeBytesReference } from "../utils/reference.js";
const TIMESTAMP_PAYLOAD_OFFSET = 0;
const TIMESTAMP_PAYLOAD_SIZE = 8;
const REFERENCE_PAYLOAD_OFFSET = TIMESTAMP_PAYLOAD_SIZE;
export function findNextIndex(ky, owner, topic, options) {
  return __awaiter(this, void 0, void 0, function* () {
    try {
      const feedUpdate = yield fetchLatestFeedUpdate(ky, owner, topic, options);
      return makeHexString(feedUpdate.feedIndexNext, FEED_INDEX_HEX_LENGTH);
    } catch (e) {
      if (e instanceof BeeResponseError && e.status === 404) {
        return bytesToHex(makeBytes(8));
      }

      throw e;
    }
  });
}
export function updateFeed(ky, signer, topic, reference, postageBatchId, options, index = 'latest') {
  var _a;

  return __awaiter(this, void 0, void 0, function* () {
    const ownerHex = makeHexEthAddress(signer.address);
    const nextIndex = index === 'latest' ? yield findNextIndex(ky, ownerHex, topic, options) : index;
    const identifier = makeFeedIdentifier(topic, nextIndex);
    const at = (_a = options === null || options === void 0 ? void 0 : options.at) !== null && _a !== void 0 ? _a : Date.now() / 1000.0;
    const timestamp = writeUint64BigEndian(at);
    const payloadBytes = serializeBytes(timestamp, reference);
    return uploadSingleOwnerChunkData(ky, signer, postageBatchId, identifier, payloadBytes, options);
  });
}
export function getFeedUpdateChunkReference(owner, topic, index) {
  const identifier = makeFeedIdentifier(topic, index);
  return keccak256Hash(identifier, owner);
}
export function downloadFeedUpdate(ky, owner, topic, index) {
  return __awaiter(this, void 0, void 0, function* () {
    const address = getFeedUpdateChunkReference(owner, topic, index);
    const addressHex = bytesToHex(address);
    const data = yield chunkAPI.download(ky, addressHex);
    const soc = makeSingleOwnerChunkFromData(data, address);
    const payload = soc.payload();
    const timestampBytes = bytesAtOffset(payload, TIMESTAMP_PAYLOAD_OFFSET, TIMESTAMP_PAYLOAD_SIZE);
    const timestamp = readUint64BigEndian(timestampBytes);
    const reference = makeBytesReference(payload, REFERENCE_PAYLOAD_OFFSET);
    return {
      timestamp,
      reference
    };
  });
}
export function makeFeedReader(ky, type, topic, owner) {
  return {
    type,
    owner,
    topic,

    download(options) {
      return __awaiter(this, void 0, void 0, function* () {
        if (!(options === null || options === void 0 ? void 0 : options.index)) {
          return fetchLatestFeedUpdate(ky, owner, topic, Object.assign(Object.assign({}, options), {
            type
          }));
        }

        const update = yield downloadFeedUpdate(ky, hexToBytes(owner), topic, options.index);
        return {
          reference: bytesToHex(update.reference),
          feedIndex: options.index,
          feedIndexNext: ''
        };
      });
    }

  };
}
export function makeFeedWriter(ky, type, topic, signer) {
  const upload = (postageBatchId, reference, options) => __awaiter(this, void 0, void 0, function* () {
    assertAddress(postageBatchId);
    const canonicalReference = makeBytesReference(reference);
    return updateFeed(ky, signer, topic, canonicalReference, postageBatchId, Object.assign(Object.assign({}, options), {
      type
    }));
  });

  return Object.assign(Object.assign({}, makeFeedReader(ky, type, topic, makeHexEthAddress(signer.address))), {
    upload
  });
}