import { promises as fs, existsSync } from "fs";
import { imageSrcToImportId, importIdToSymbolName } from "../assets/utils/resolveImports.js";
import { AstroError, AstroErrorData } from "../core/errors/index.js";
const SAVE_DEBOUNCE_MS = 500;
class DataStore {
  #collections = /* @__PURE__ */ new Map();
  #file;
  #assetsFile;
  #saveTimeout;
  #assetsSaveTimeout;
  #dirty = false;
  #assetsDirty = false;
  #assetImports = /* @__PURE__ */ new Set();
  constructor() {
    this.#collections = /* @__PURE__ */ new Map();
  }
  get(collectionName, key) {
    return this.#collections.get(collectionName)?.get(String(key));
  }
  entries(collectionName) {
    const collection = this.#collections.get(collectionName) ?? /* @__PURE__ */ new Map();
    return [...collection.entries()];
  }
  values(collectionName) {
    const collection = this.#collections.get(collectionName) ?? /* @__PURE__ */ new Map();
    return [...collection.values()];
  }
  keys(collectionName) {
    const collection = this.#collections.get(collectionName) ?? /* @__PURE__ */ new Map();
    return [...collection.keys()];
  }
  set(collectionName, key, value) {
    const collection = this.#collections.get(collectionName) ?? /* @__PURE__ */ new Map();
    collection.set(String(key), value);
    this.#collections.set(collectionName, collection);
    this.#saveToDiskDebounced();
  }
  delete(collectionName, key) {
    const collection = this.#collections.get(collectionName);
    if (collection) {
      collection.delete(String(key));
      this.#saveToDiskDebounced();
    }
  }
  clear(collectionName) {
    this.#collections.delete(collectionName);
    this.#saveToDiskDebounced();
  }
  has(collectionName, key) {
    const collection = this.#collections.get(collectionName);
    if (collection) {
      return collection.has(String(key));
    }
    return false;
  }
  hasCollection(collectionName) {
    return this.#collections.has(collectionName);
  }
  collections() {
    return this.#collections;
  }
  addAssetImport(assetImport, filePath) {
    const id = imageSrcToImportId(assetImport, filePath);
    if (id) {
      this.#assetImports.add(id);
      this.#writeAssetsImportsDebounced();
    }
  }
  addAssetImports(assets, filePath) {
    assets.forEach((asset) => this.addAssetImport(asset, filePath));
  }
  async writeAssetImports(filePath) {
    this.#assetsFile = filePath;
    if (this.#assetImports.size === 0) {
      try {
        await fs.writeFile(filePath, "export default new Map();");
      } catch (err) {
        throw new AstroError({
          ...err,
          ...AstroErrorData.ContentLayerWriteError
        });
      }
    }
    if (!this.#assetsDirty && existsSync(filePath)) {
      return;
    }
    const imports = [];
    const exports = [];
    this.#assetImports.forEach((id) => {
      const symbol = importIdToSymbolName(id);
      imports.push(`import ${symbol} from '${id}';`);
      exports.push(`[${JSON.stringify(id)}, ${symbol}]`);
    });
    const code = (
      /* js */
      `
${imports.join("\n")}
export default new Map([${exports.join(", ")}]);
		`
    );
    try {
      await fs.writeFile(filePath, code);
    } catch (err) {
      throw new AstroError({
        ...err,
        ...AstroErrorData.ContentLayerWriteError
      });
    }
    this.#assetsDirty = false;
  }
  #writeAssetsImportsDebounced() {
    this.#assetsDirty = true;
    if (this.#assetsFile) {
      if (this.#assetsSaveTimeout) {
        clearTimeout(this.#assetsSaveTimeout);
      }
      this.#assetsSaveTimeout = setTimeout(() => {
        this.#assetsSaveTimeout = void 0;
        this.writeAssetImports(this.#assetsFile);
      }, SAVE_DEBOUNCE_MS);
    }
  }
  #saveToDiskDebounced() {
    this.#dirty = true;
    if (this.#file) {
      if (this.#saveTimeout) {
        clearTimeout(this.#saveTimeout);
      }
      this.#saveTimeout = setTimeout(() => {
        this.#saveTimeout = void 0;
        this.writeToDisk(this.#file);
      }, SAVE_DEBOUNCE_MS);
    }
  }
  scopedStore(collectionName) {
    return {
      get: (key) => this.get(collectionName, key),
      entries: () => this.entries(collectionName),
      values: () => this.values(collectionName),
      keys: () => this.keys(collectionName),
      set: ({ id: key, data, body, filePath, digest, rendered }) => {
        if (!key) {
          throw new Error(`ID must be a non-empty string`);
        }
        const id = String(key);
        if (digest) {
          const existing = this.get(collectionName, id);
          if (existing && existing.digest === digest) {
            return false;
          }
        }
        const entry = {
          id,
          data
        };
        if (body) {
          entry.body = body;
        }
        if (filePath) {
          if (filePath.startsWith("/")) {
            throw new Error(`File path must be relative to the site root. Got: ${filePath}`);
          }
          entry.filePath = filePath;
        }
        if (digest) {
          entry.digest = digest;
        }
        if (rendered) {
          entry.rendered = rendered;
        }
        this.set(collectionName, id, entry);
        return true;
      },
      delete: (key) => this.delete(collectionName, key),
      clear: () => this.clear(collectionName),
      has: (key) => this.has(collectionName, key),
      addAssetImport: (assetImport, fileName) => this.addAssetImport(assetImport, fileName),
      addAssetImports: (assets, fileName) => this.addAssetImports(assets, fileName)
    };
  }
  metaStore(collectionName) {
    const collectionKey = `meta:${collectionName}`;
    return {
      get: (key) => this.get(collectionKey, key),
      set: (key, data) => this.set(collectionKey, key, data),
      delete: (key) => this.delete(collectionKey, key),
      has: (key) => this.has(collectionKey, key)
    };
  }
  toString() {
    return JSON.stringify(
      Array.from(this.#collections.entries()).map(([collectionName, collection]) => {
        return [collectionName, Array.from(collection.entries())];
      })
    );
  }
  async writeToDisk(filePath) {
    if (!this.#dirty) {
      return;
    }
    try {
      await fs.writeFile(filePath, this.toString());
      this.#file = filePath;
      this.#dirty = false;
    } catch (err) {
      throw new AstroError({
        ...err,
        ...AstroErrorData.ContentLayerWriteError
      });
    }
  }
  static async fromDisk(filePath) {
    if (!existsSync(filePath)) {
      return new DataStore();
    }
    const str = await fs.readFile(filePath, "utf-8");
    return DataStore.fromString(str);
  }
  static fromString(str) {
    const entries = JSON.parse(str);
    return DataStore.fromJSON(entries);
  }
  static async fromModule() {
    try {
      const data = await import("astro:data-layer-content");
      return DataStore.fromJSON(data.default);
    } catch {
    }
    return new DataStore();
  }
  static fromJSON(entries) {
    const collections = /* @__PURE__ */ new Map();
    for (const [collectionName, collection] of entries) {
      collections.set(collectionName, new Map(collection));
    }
    const store = new DataStore();
    store.#collections = collections;
    return store;
  }
}
function dataStoreSingleton() {
  let instance = void 0;
  return {
    get: async () => {
      if (!instance) {
        instance = DataStore.fromModule();
      }
      return instance;
    },
    set: (store) => {
      instance = store;
    }
  };
}
const globalDataStore = dataStoreSingleton();
export {
  DataStore,
  globalDataStore
};
