import ensurePlainObject from 'type/plain-object/ensure.js'
import ensureString from 'type/string/ensure.js'
import path from 'path'
import fsp from 'fs/promises'
import ServerlessError from '../serverless-error.js'
import fileExists from '../utils/fs/file-exists.js'
import resolveInput from './resolve-input.js'

const supportedExtensions = new Set([
  'yml',
  'yaml',
  'json',
  'js',
  'mjs',
  'cjs',
  'ts',
])

export default async (options = {}) => {
  if (!options) options = {}
  const cliOptions =
    ensurePlainObject(options.options, { isOptional: true }) ||
    resolveInput().options
  const cwd = path.resolve(
    ensureString(options.cwd, { isOptional: true }) || process.cwd(),
  )
  const customConfigName = cliOptions.config

  if (customConfigName) {
    const customConfigPath = path.resolve(cwd, customConfigName)
    if (!supportedExtensions.has(path.extname(customConfigPath).slice(1))) {
      throw new ServerlessError(
        'Invalid "--config" value: Unsupported file extension ' +
          `(expected one of: ${Array.from(supportedExtensions).join(', ')}`,
        'INVALID_SERVICE_CONFIG_PATH',
      )
    }
    const stats = await (async () => {
      try {
        return await fsp.stat(customConfigPath)
      } catch (error) {
        if (error.code === 'ENOENT') {
          throw new ServerlessError(
            `'Invalid "--config" value: Cannot find "${customConfigName}" in service folder`,
            'INVALID_SERVICE_CONFIG_PATH',
          )
        }
        throw new ServerlessError(
          `Invalid "--config" value: "${customConfigName}" is not accessible: ${error.stack}`,
          'INVALID_SERVICE_CONFIG_PATH',
        )
      }
    })()
    if (!stats.isFile()) {
      throw new ServerlessError(
        `Invalid "--config" value: "${customConfigName}" is not a file`,
        'INVALID_SERVICE_CONFIG_PATH',
      )
    }
    if (cwd !== path.dirname(customConfigPath)) {
      throw new ServerlessError(
        'Service configuration is expected to be placed in a root of a service (working directory). All paths, function handlers in a configuration are resolved against service directory.\n',
        'NESTED_CUSTOM_CONFIGURATION_PATH',
      )
    }
    return customConfigPath
  }

  for (const extension of supportedExtensions) {
    const eventualServiceConfigPath = path.resolve(
      cwd,
      `serverless.${extension}`,
    )
    if (await fileExists(eventualServiceConfigPath))
      return eventualServiceConfigPath
  }
  return null
}
