const awspublish = require('gulp-awspublish')
const human = require('human-interval')
const la = require('lazy-ass')
const check = require('check-more-types')
const fse = require('fs-extra')
const os = require('os')
const Promise = require('bluebird')
const { fromSSO, fromEnv } = require('@aws-sdk/credential-providers')
const _ = require('lodash')

const { purgeCloudflareCache } = require('./purge-cloudflare-cache')

const CDN_URL = 'https://cdn.cypress.io'

const getUploadUrl = function () {
  const url = CDN_URL

  la(check.url(url), 'could not get CDN url', url)

  return url
}

const formHashFromEnvironment = function () {
  const {
    env,
  } = process

  if (env.CIRCLECI) {
    return `${env.CIRCLE_BRANCH}-${env.CIRCLE_SHA1}`
  }

  throw new Error('Do not know how to form unique build hash on this CI')
}

const S3Configuration = {
  bucket: 'cdn.cypress.io',
  releaseFolder: 'desktop',
  binaryZipName: 'cypress.zip',
  betaUploadTypes: {
    binary: {
      uploadFolder: 'binary',
      uploadFileName: 'cypress.zip',
    },
    'npm-package': {
      uploadFolder: 'npm',
      uploadFileName: 'cypress.tgz',
    },
  },
}

const getS3Credentials = async function () {
  // sso is not required for CirceCI
  if (process.env.CIRCLECI) {
    return fromEnv()()
  }

  // use 'prod' by default to align with our internal docs for setting up `awscli`
  // https://cypress-io.atlassian.net/wiki/spaces/INFRA/pages/1534853121/AWS+SSO+Cypress
  return fromSSO({ profile: process.env.AWS_PROFILE || 'prod' })()
}

const getPublisher = async function () {
  const aws = await getS3Credentials()

  return awspublish.create({
    httpOptions: {
      timeout: human('10 minutes'),
    },
    params: {
      Bucket: S3Configuration.bucket,
    },
    credentials: {
      accessKeyId: aws.accessKeyId,
      secretAccessKey: aws.secretAccessKey,
      sessionToken: aws.sessionToken,
    },
    region: 'us-east-1',
  })
}

const getDesktopUrl = function (version, osName, zipName) {
  const url = getUploadUrl()

  return [url, 'desktop', version, osName, zipName].join('/')
}

const purgeDesktopManifest = function () {
  const url = getUploadUrl()

  return purgeCloudflareCache([url, 'desktop', 'manifest.json'].join('/'))
}

// purges desktop application url from Cloudflare cache
const purgeDesktopAppFromCache = function ({ version, platformArch, zipName }) {
  la(check.unemptyString(version), 'missing desktop version', version)
  la(check.unemptyString(platformArch), 'missing platformArch', platformArch)
  la(check.unemptyString(zipName), 'missing zip filename')
  la(check.extension('zip', zipName),
    'zip filename should end with .zip', zipName)

  const url = getDesktopUrl(version, platformArch, zipName)

  return purgeCloudflareCache(url)
}

// all architectures we are building the test runner for
const validPlatformArchs = ['darwin-arm64', 'darwin-x64', 'linux-x64', 'linux-arm64', 'win32-x64']

// purges links to desktop app for all platforms
// for a given version
const purgeDesktopAppAllPlatforms = function (version, zipName) {
  la(check.unemptyString(version), 'missing desktop version', version)
  la(check.unemptyString(zipName), 'missing zipName', zipName)

  console.log(`purging all desktop links for version ${version} from Cloudflare`)

  return Promise.mapSeries(validPlatformArchs, (platformArch) => {
    return purgeDesktopAppFromCache({ version, platformArch, zipName })
  })
}

// reads all lines in from a file as URLs and purges them from Cloudflare
// see Slack conversation https://cypressio.slack.com/archives/C055U0SMV32/p1695851503262999?thread_ts=1695846392.723289&cid=C055U0SMV32
const purgeUrlsFromCloudflareCache = async function (urlsFilePath) {
  const urls = (await fse.readFile(urlsFilePath)).toString().split('\n')

  return Promise.map(_.compact(urls), purgeCloudflareCache, { concurrency: 5 })
}

// simple check for platform-arch string
// example: isValidPlatformArch("darwin") // FALSE
const isValidPlatformArch = check.oneOf(validPlatformArchs)

const getValidPlatformArchs = () => {
  return validPlatformArchs
}

const getUploadNameByOsAndArch = function (platform) {
  const arch = os.arch()

  const name = [platform, arch].join('-')

  if (!isValidPlatformArch(name)) {
    throw new Error(`${name} is not a valid upload destination. Does validPlatformArchs need updating?`)
  }

  la(isValidPlatformArch(name), 'formed invalid platform', name, 'from', platform, arch)

  return name
}

const saveUrl = (filename) => {
  return (function (url) {
    la(check.unemptyString(filename), 'missing filename', filename)
    la(check.url(url), 'invalid url to save', url)
    const s = JSON.stringify({ url })

    return fse.writeFile(filename, s)
    .then(() => {
      return console.log('saved url', url, 'into file', filename)
    })
  })
}

module.exports = {
  S3Configuration,
  getS3Credentials,
  getPublisher,
  purgeDesktopAppFromCache,
  purgeDesktopAppAllPlatforms,
  purgeDesktopManifest,
  getUploadNameByOsAndArch,
  validPlatformArchs,
  getValidPlatformArchs,
  isValidPlatformArch,
  saveUrl,
  formHashFromEnvironment,
  getUploadUrl,
  purgeUrlsFromCloudflareCache,
}
