import * as path from 'path'
import * as fs from 'fs-extra'
import * as postcss from 'postcss'
import * as pxtransform from 'postcss-pxtransform'
import transformCSS from 'taro-css-to-react-native'
import {
  FILE_PROCESSOR_MAP,
  npm as npmProcess,
  processTypeEnum,
  printLog,
  chalk
} from '@tarojs/helper'

import { StyleSheetValidation } from './StyleSheet/index'

import * as stylelintConfig from '../config/rn-stylelint.json'

const DEVICE_RATIO = 'deviceRatio'

function getWrapedCSS (css) {
  return `
import { StyleSheet, Dimensions } from 'react-native'

// 一般app 只有竖屏模式，所以可以只获取一次 width
const deviceWidthDp = Dimensions.get('window').width
const uiWidthPx = 375

function scalePx2dp (uiElementPx) {
  return uiElementPx * deviceWidthDp / uiWidthPx
}

export default StyleSheet.create(${css})
`
}

/**
 * @description 读取 css/scss/less 文件，预处理后，返回 css string
 * @param {string} filePath
 * @param {object} pluginsConfig
 * @param {string} appPath
 * @returns {*}
 */
function loadStyle ({ filePath, pluginsConfig }, appPath) {
  const fileExt = path.extname(filePath)
  const pluginName = FILE_PROCESSOR_MAP[fileExt]
  if (pluginName) {
    return npmProcess
      .callPlugin(pluginName, null, filePath, pluginsConfig[pluginName] || {}, appPath)
      .then(item => {
        return {
          css: item.css.toString(),
          filePath
        }
      })
      .catch(e => {
        printLog(processTypeEnum.ERROR, '样式预处理', filePath)
        console.log(e.stack)
      })
  }
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf-8', (err, content) => {
      if (err) {
        return reject(err)
      }
      resolve({
        css: content,
        filePath
      })
    })
  })
}

/**
 * @description 传入 css string ，返回 postCSS 处理后的 css string
 * @param {string} css
 * @param {string} filePath
 * @param {object} projectConfig
 * @returns {Function | any}
 */
function postCSS ({ css, filePath, projectConfig }) {
  const pxTransformConfig = {
    designWidth: projectConfig.designWidth || 750
  }
  if (projectConfig.hasOwnProperty(DEVICE_RATIO)) {
    pxTransformConfig[DEVICE_RATIO] = projectConfig.deviceRatio
  }
  return postcss([
    require('stylelint')(stylelintConfig),
    require('postcss-reporter')({ clearReportedMessages: true }),
    pxtransform({
      platform: 'rn',
      ...pxTransformConfig
    })
  ])
    .process(css, { from: filePath })
    .then(result => {
      return {
        css: result.css,
        filePath
      }
    })
    .catch(e => {
      printLog(processTypeEnum.ERROR, '样式转换', filePath)
      console.log(e.stack)
    })
}

function getStyleObject ({ css, filePath }) {
  let styleObject = {}
  try {
    styleObject = transformCSS(css)
  } catch (err) {
    printLog(processTypeEnum.WARNING, 'css-to-react-native 报错', filePath)
    console.log(chalk.red(err.stack))
  }
  return styleObject
}

function validateStyle ({ styleObject, filePath }) {
  for (const name in styleObject) {
    try {
      StyleSheetValidation.validateStyle(name, styleObject)
    } catch (err) {
      // 先忽略掉 scalePx2dp 的报错
      if (/Invalid prop `.*` of type `string` supplied to `.*`, expected `number`[^]*/g.test(err.message)) return
      printLog(processTypeEnum.WARNING, '样式不支持', filePath)
      console.log(chalk.red(err.message))
    }
  }
}

function writeStyleFile ({ css, tempFilePath }) {
  const fileContent = getWrapedCSS(css.replace(/"(scalePx2dp\(.*?\))"/g, '$1'))
  fs.ensureDirSync(path.dirname(tempFilePath))
  fs.writeFileSync(tempFilePath, fileContent)
  printLog(processTypeEnum.GENERATE, '生成样式文件', tempFilePath)
}

export { loadStyle, postCSS, getStyleObject, validateStyle, writeStyleFile }
