const fs = require('fs/promises')
const path = require('path')

/**
 * AbstractJsonRepository
 *
 * Defines the storage contract for JSON-backed repositories.
 */
class AbstractJsonRepository {
  constructor(dataDirectory) {
    if (new.target === AbstractJsonRepository) {
      throw new Error('AbstractJsonRepository cannot be instantiated directly')
    }
    this._dataDirectory = dataDirectory
  }

  /**
   * @returns {Promise<void>}
   */
  async ensureDirectory() {
    await fs.mkdir(this._dataDirectory, { recursive: true })
  }

  /**
   * @param {string} fileName
   * @returns {string}
   */
  resolvePath(fileName) {
    return path.join(this._dataDirectory, fileName)
  }

  /**
   * @param {string} fileName
   * @param {object} payload
   * @returns {Promise<void>}
   */
  async writeJson(fileName, payload) {
    await this.ensureDirectory()
    await fs.writeFile(this.resolvePath(fileName), JSON.stringify(payload, null, 2), 'utf8')
  }

  /**
   * @param {string} fileName
   * @param {object} fallback
   * @returns {Promise<object>}
   */
  async readJson(fileName, fallback) {
    await this.ensureDirectory()
    try {
      const fileContent = await fs.readFile(this.resolvePath(fileName), 'utf8')
      return JSON.parse(fileContent)
    } catch (error) {
      if (error.code === 'ENOENT') {
        return fallback
      }
      throw error
    }
  }
}

module.exports = {
  AbstractJsonRepository
}