import testRule from 'skypager-util/lib/path-matcher'
import pick from 'lodash/pick'
import omit from 'lodash/omit' 
import mapValues from 'lodash/mapValues' 
import omitBy from 'lodash/omitBy' 
import isEmpty from 'lodash/isEmpty' 
import docblock from 'docblock-parser'
import entity from 'skypager-util/lib/entity'

export const docblockParser = docblock({
  tags: {
    name: docblock.singleParameterTag
  }
})

export const rules = [
  /\.(js|es6|jsx)$/
]

export const testDocument = (doc) => (
  typeof rules.find(rule => testRule(rule, doc.fileBaseName)) !== 'undefined'
)

export const compile = (code, options) => 
  transpile(code, options).code

export const toAST = (code, options) =>
  require('babel-core').transform(code, babelOptions(options)).ast

export const transpile = (code = '', options = {}) => (
  require('babel-core').transform(code, babelOptions(options))
)
    
export const decorate = (doc) => {
  Object.assign(doc, {
    toEntity() {
      const exportables = doc.exportables
      const frontmatter = exportables.frontmatter

      return (Object.assign({id: doc.id}, frontmatter, {
        imports: exportables.imports,
        exports: exportables.exports,
      }))
    },

    /**
      multilineTilTag
      multilineTilEmptyLineOrTag
      booleanTag
      singleParameterTag
      multiParameterTag(delimiter)
    */ 
    docblockParser(options = {}) {
      const parser = docblock({
        tags: Object.assign({
          name: docblock.singleParameterTag,              
        }, options)
      })    

      return parser.parse
    },

    createExportable() {
      const exportable = entity({
        id: doc.id,
        file: omit(doc.file, 'contents'),
      })

      exportable.lazy('babel', () => doc.transformBabel())
      exportable.lazy('ast', () => exportable.babel.ast)
      exportable.lazy('code', () => exportable.babel.code)
      exportable.lazy('meta', () => exportable.babel.metadata)
      
      exportable.lazy('commentNodes', () => exportable.get('ast.comments', []))
      
      exportable.lazy('imports', () => exportable.get('babel.metadata.modules.imports', []).map(m => m.source), true)
      exportable.lazy('exports', () => exportable.get('babel.metadata.modules.exports.exported', []), true)

      exportable.getter('contents', () => doc.readFile().getFileContents())

      exportable.lazy('frontmatter', () => {
        try {
          const result = docblockParser.parse(exportable.get('ast.comments[0].value', "/**\n**/"))
          const { tags = {}, text = '' } = result
          const values = mapValues(Object.assign({text, id: doc.id}, tags), (v) => (
            typeof v === 'string' ? v.trim() : v            
          ))

          return omitBy(values, (v) => isEmpty(v))
        } catch(error) {
          return {}
        }
      }, true)

      return exportable
    },

    readFrontmatter() {
      return doc.exportables.frontmatter
    },

    transformBabel(options) {
      options = options || doc.project.babelRc || {presets:['skypager']}
      const code = options.code || doc.file.contents.toString()
      return require('babel-core').transform(code, babelOptions(options))
    }
  })

  doc.lazy('exportables', () => doc.createExportable())

  return doc  
} 

export default decorate

const babelOptions = (options) => {
  return pick(options, "filename","filenameRelative","presets","plugins","parserOpts","generatorOpts","highlightCode","only","ignore","auxiliaryCommentBefore","auxiliaryCommentAfter","sourceMaps","inputSourceMap","sourceMapTarget","sourceFileName","sourceRoot","moduleRoot","moduleIds","moduleId","getModuleId","resolveModuleSource","code","no-babelrc","ast","compact","minified","comments","shouldPrintComment","env","retainLines","extends")
}
