/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 * @oncall react_native
 */

import type {
  BuildParameters,
  CacheData,
  CacheDelta,
  CacheManager,
} from '../flow-types';

import rootRelativeCacheKeys from '../lib/rootRelativeCacheKeys';
import {readFileSync, writeFileSync} from 'graceful-fs';
import {tmpdir} from 'os';
import path from 'path';
import {deserialize, serialize} from 'v8';

type DiskCacheConfig = {
  buildParameters: BuildParameters,
  cacheFilePrefix?: ?string,
  cacheDirectory?: ?string,
};

const DEFAULT_PREFIX = 'metro-file-map';
const DEFAULT_DIRECTORY = tmpdir();

export class DiskCacheManager implements CacheManager {
  _cachePath: string;

  constructor({
    buildParameters,
    cacheDirectory,
    cacheFilePrefix,
  }: DiskCacheConfig) {
    this._cachePath = DiskCacheManager.getCacheFilePath(
      buildParameters,
      cacheFilePrefix,
      cacheDirectory,
    );
  }

  static getCacheFilePath(
    buildParameters: BuildParameters,
    cacheFilePrefix?: ?string,
    cacheDirectory?: ?string,
  ): string {
    const {rootDirHash, relativeConfigHash} =
      rootRelativeCacheKeys(buildParameters);

    return path.join(
      cacheDirectory ?? DEFAULT_DIRECTORY,
      `${
        cacheFilePrefix ?? DEFAULT_PREFIX
      }-${rootDirHash}-${relativeConfigHash}`,
    );
  }

  getCacheFilePath(): string {
    return this._cachePath;
  }

  async read(): Promise<?CacheData> {
    try {
      return deserialize(readFileSync(this._cachePath));
    } catch (e) {
      if (e?.code === 'ENOENT') {
        // Cache file not found - not considered an error.
        return null;
      }
      // Rethrow anything else.
      throw e;
    }
  }

  async write(
    dataSnapshot: CacheData,
    {changed, removed}: CacheDelta,
  ): Promise<void> {
    if (changed.size > 0 || removed.size > 0) {
      writeFileSync(this._cachePath, serialize(dataSnapshot));
    }
  }
}
