const walk = require("walk")
const gm = require("gm")
const _ = require("lodash/fp")
const FormData = require("form-data")
const fetch = require("node-fetch")
const fs = require("fs")
const path = require("path")
const https = require("https");
const agent = new https.Agent({
  rejectUnauthorized: false
})

const resourcesFactory = async (api, config, location, opts = {}) => {
  let {id: blockId} = config
  if(opts.blockId) {
    blockId = opts.blockId
  }

  const blockResources = (await api.block.listResources({ownerId: blockId, query: {limit: 1000, select: "ref signature"}})).data

  let toRemove = blockResources.map(({_id}) => _id)
  const _resources = []

  const identify = image => new Promise((resolve, reject) => image.identify((err, identity) => {
    if(err) return reject(err);
    const signature = _.get(["Properties","signature"])(identity)
    const mimeType = identity["Mime Type"]
    resolve({signature, mimeType})
  }))

  const walkLocation = () => new Promise((resolve) => {
    const assetsPath = path.join(location, 'assets')
    const walker = walk.walk(assetsPath, {});

    walker.on('file', (root, fileStats, next) => {
      const resourcePath = path.join(root, fileStats.name)
      const relativePath = resourcePath.replace(assetsPath + '/', '')
      if (!fileStats.name.match(/.(jpg|jpeg|png)$/i)) {
        return next()
      }

      const image = gm.subClass({imageMagick: true})(resourcePath)
      const result = {
        relativePath,
        resourcePath,
        name: fileStats.name,
        signature: '',
        mimeType: 'image/jpeg'
      }
      _resources.push(result)
      identify(image)
        .then(({signature, mimeType}) => Object.assign(result, {signature, mimeType}))
        .catch(console.error)
        .then(next)
    })
    walker.on('errors', (root, nodeStats, next) => {
      next()
    })
    walker.on('end', () => {
      resolve()
    })
  })

  const filterResources = () => {
    const res = _.filter((resource) => {
      const findedResource = _.find((r) => r.signature === resource.signature)(blockResources)
      if(findedResource) {
        Object.assign(resource, findedResource)
        toRemove = toRemove.filter((id) => id !== findedResource._id)
      }
      return !findedResource
    })(_resources)
    return res
  }

  const uploadResources = async () => {
    const resources = filterResources()
    const upload = async (resource) => {
      const URL = `${api.host}/api/block/${blockId}/resource`
      const {resourcePath, name, mimeType} = resource
      const form = new FormData()
      form.append(name, fs.createReadStream(resourcePath), {
        filename: name,
        contentType: mimeType
      })
      const headers = form.getHeaders()
      api.token && (headers.authorization = "Bearer " + api.token)
      const fetchAgent =  api.host.startsWith('https://') ? {agent} : {}
      const response = await fetch(URL, {method: "POST", body: form, headers, ...fetchAgent});

      if (response.status >= 300) {
        throw new Error(await response.text())
      }
      return response.json()
    }

    for (const i in resources) {
      const resource = resources[i]
      const {data: [newResource]} = await upload(resource)
      const findedResource = _resources.find(({signature}) => resource.signature === signature)
      Object.assign(findedResource, newResource)
    }

  }

  const removeResources = () =>
    Promise.all(toRemove.map(id => api.block.removeResource({ownerId: blockId, id})))

  const urlLoader = (url) => {
    const resource = _resources.find(({relativePath}) => `/${relativePath}` === url)
    return resource ? resource.url : url
  }

  const replaceResources = (view) => {
    if (!_resources.length) {
      return view
    }
    const mapping = _resources.map(({relativePath}) => relativePath.replace(/([./\\\][])/g, '\\$1')).join('|')
    const regExp = RegExp(`"(${mapping})"|'(${mapping})'`, "gm");
    const replacer = (match, key, key1) => {
      const img = key || key1
      const resource = _resources.find(({relativePath}) => relativePath === img)
      if (resource) {
        return `"${resource.ref}"`
      }
      return img
    }
    const replacedView = view.replace(regExp, replacer)
    return replacedView
  }

  try {
    await walkLocation()
    await uploadResources()
    await removeResources()
  } catch (error) {
    console.error(error)
    process.exit(42)
  }

  return {urlLoader, replaceResources}
}

module.exports = resourcesFactory
