/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * 
 */
'use strict';

const ReactElementPlugin = require('pretty-format/plugins/ReactElement');
const ReactTestComponentPlugin = require('pretty-format/plugins/ReactTestComponent');

const createDirectory = require('jest-util').createDirectory;
const fileExists = require('jest-file-exists');
const fs = require('fs');
const path = require('path');
const prettyFormat = require('pretty-format');

const jsxLikePlugins = [ReactElementPlugin, ReactTestComponentPlugin];
const SNAPSHOT_EXTENSION = 'snap';


















const ensureDirectoryExists = filePath => {
  try {
    createDirectory(path.join(path.dirname(filePath)));
  } catch (e) {}
};

const escape = string => string.replace(/\`/g, '\\\`');
const unescape = string => string.replace(/\\(\"|\\|\')/g, '$1');

// Extra line breaks at the beginning and at the end of the snapshot are useful
// to make the content of the snapshot easier to read
const addExtraLineBreaks =
string => string.includes('\n') ? `\n${ string }\n` : string;

class SnapshotFile {






  constructor(filename) {
    this._filename = filename;
    this._dirty = false;

    this._content = Object.create(null);
    if (this.fileExists(filename)) {
      try {
        delete require.cache[require.resolve(filename)];
        /* eslint-disable no-useless-call */
        Object.assign(this._content, require.call(null, filename));
        /* eslint-enable no-useless-call */
      } catch (e) {}
    }
    this._uncheckedKeys = new Set(Object.keys(this._content));
  }

  getUncheckedCount() {
    return this._uncheckedKeys.size || 0;
  }

  fileExists() {
    return fileExists(this._filename);
  }

  removeUncheckedKeys() {
    if (this._uncheckedKeys.size) {
      this._dirty = true;
      this._uncheckedKeys.forEach(key => delete this._content[key]);
      this._uncheckedKeys.clear();
    }
  }

  serialize(data) {
    return addExtraLineBreaks(prettyFormat(data, {
      plugins: jsxLikePlugins }));

  }

  save(update) {
    const status = {
      deleted: false,
      saved: false };


    const isEmpty = Object.keys(this._content).length === 0;
    if ((this._dirty || this._uncheckedKeys.size) && !isEmpty) {
      const snapshots = Object.keys(this._content).sort().map(key =>
      'exports[`' + escape(key) + '`] = `' +
      escape(this._content[key]) + '`;');


      ensureDirectoryExists(this._filename);
      fs.writeFileSync(this._filename, snapshots.join('\n\n') + '\n');
      status.saved = true;
    } else if (isEmpty && this.fileExists()) {
      if (update) {
        fs.unlinkSync(this._filename);
      }
      status.deleted = true;
    }

    return status;
  }

  has(key) {
    return this._content[key] !== undefined;
  }

  get(key) {
    return this._content[key];
  }

  matches(key, value) {
    this._uncheckedKeys.delete(key);
    const serialized = this.serialize(value);
    const actual = unescape(serialized);
    const expected = this.get(key);
    const pass = expected === actual;
    if (pass) {
      // Executing a snapshot file as JavaScript and writing the strings back
      // when other snapshots have changed loses the proper escaping for some
      // characters. Since we check every snapshot in every test, use the newly
      // generated formatted string.
      // Note that this is only relevant when a snapshot is added and the dirty
      // flag is set.
      this._content[key] = serialized;
    }
    return {
      actual,
      expected,
      pass };

  }

  add(key, value) {
    this._dirty = true;
    this._uncheckedKeys.delete(key);
    this._content[key] = this.serialize(value);
  }}



module.exports = {
  SNAPSHOT_EXTENSION,
  forFile(testPath) {
    const snapshotsPath = path.join(path.dirname(testPath), '__snapshots__');

    const snapshotFilename = path.join(
    snapshotsPath,
    path.basename(testPath) + '.' + SNAPSHOT_EXTENSION);


    return new SnapshotFile(snapshotFilename);
  },
  SnapshotFile };