import fs from 'fs'
import path from 'path'
import trash from 'trash'
import config from '../config'
import { log } from '../ulits/log'

interface BaseInterface {

  getProjectConfig(): Promise<boolean>

  fileIsRepeat(msg: string): void

  getAppJson(): Promise<boolean>

  checkFileIsExists(root: string): boolean

  readDirs(root: string): Promise<string[]>

  readFile(root: string, file: string): Promise<string>

  writeFile<T>(root: string, file: string, content: any): Promise<T>

  makeDir<T>(src: string): Promise<T>

  copyFile(originpath: string, curpath: string, file: string): Promise<string>

  copyFilesArr(originpath: string, curpath: string, files: string[]): Promise<any>

  rmFile(root: string, file: string): Promise<void>

  rmDir(root: string): Promise<void>

  trash(root: string[] | string): Promise<void>

}

interface SubListType {
  path: string
  subpackage: boolean
  index?: number
}

export default abstract class Base implements BaseInterface {
  protected root: string
  protected projectRoot: string
  protected templateRoot: string
  protected miniprogramRoot: string
  protected pageRoot: string
  protected componentRoot: string
  protected subpackageRoot: string

  protected typeName: string = '页面'
  protected typeRoot: string = ''
  protected mainRoots: string[] = []
  protected subRoots: string[] = []
  protected subChild: object = {}
  protected subList: Array<SubListType> = []
  protected projectMode: 'javascript' | 'typescript' = 'javascript'
  protected projectCss: 'wxss' | 'less' = 'wxss'
  protected projectConfigJson: any
  protected AppJson: any

  protected constructor() {
    this.root = config.root
    this.projectRoot = config.projectRoot
    this.templateRoot = config.templateRoot
    this.miniprogramRoot = config.miniprogramRoot
    this.pageRoot = config.pageRoot
    this.componentRoot = config.componentRoot
    this.subpackageRoot = config.subpackageRoot
  }

  // 获取项目配置文件
  public getProjectConfig(): Promise<boolean> {
    return new Promise(resolve => {
      this.readFile(this.projectRoot, 'project.config.json').then(file => {
        let projectConfigJson: any = JSON.parse(file)
        let {projectmode, projectcss} = projectConfigJson
        this.projectMode = projectmode
        this.projectCss = projectcss
        this.projectConfigJson = projectConfigJson
        resolve(true)
      }).catch(_error => {
        log.error('打开project.config.json失败，请确认当前目录是否正确！', false)
      })
    })
  }

  // 获取App.json
  public getAppJson(): Promise<boolean> {
    return new Promise(resolve => {
      this.readFile(this.projectRoot, 'app.json').then(file => {
        const {components, subpackages, pages} = JSON.parse(file)
        const type = this.typeRoot === 'pages' ? pages : components
        this.AppJson = JSON.parse(file)
        this.mainRoots = type.map(v => {
          this.subList.push({path: v, subpackage: false})
          return v.split('/')[1]
        })
        subpackages.map(({root, components, pages}, k) => {
          const type = this.typeRoot === 'pages' ? pages : components
          let children = type.map(s => {
            this.subList.push({path: path.join(root, s), subpackage: true, index: k})
            return s.split('/')[1]
          })
          this.subRoots.push(root)
          this.subChild[root] = children
        })
        resolve(true)
      }).catch(_error => {
        log.error('打开app.json失败，请确认当前目录是否正确！', false)
      })
    })
  }

  public fileIsRepeat(msg: string): void {
    log.warning(`文件【${msg}】已存在，默认覆盖`, false)
  }

  public checkFileIsExists(root: string): boolean {
    return fs.existsSync(root)
  }

  public readDirs(root: string): Promise<string[]> {
    return new Promise((resolve, reject) => {
      try {
        let files = fs.readdirSync(root)
        let fileList = files.filter((v) => {
          return fs.statSync(path.resolve(root, v)).isFile() && v !== '.DS_Store'
        })
        resolve(fileList)
      } catch (e) {
        reject(e)
      }
    })
  }

  public readFile(root: string, file: string): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      try {
        let content = fs.readFileSync(path.join(root, file))
        resolve(content.toString())
      } catch (e) {
        reject(e)
      }
    })
  }

  writeFile<T>(root: string, file: string, content: any): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      try {
        fs.writeFileSync(path.join(root, file), content)
        resolve()
      } catch (e) {
        reject(e)
      }
    })
  }

  public makeDir<T>(src: string): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      try {
        fs.mkdirSync(src, {recursive: true})
        resolve()
      } catch (e) {
        reject(e)
      }
    })
  }

  public copyFile(originpath: string, curpath: string, file: string): Promise<string> {
    return new Promise((resolve, reject) => {
      try {
        fs.copyFileSync(path.join(originpath, file), path.join(curpath, file), fs.constants.COPYFILE_EXCL)
        resolve(file)
      } catch (e) {
        reject(e)
      }
    })
  }

  public copyFilesArr(originpath: string, curpath: string, files: string[]): Promise<any> {
    return new Promise<any>((resolve, reject) => {
      files.map(v => {
        this.copyFile(originpath, curpath, v)
          .then(v => {
            resolve(v)
          })
          .catch(error => reject({error, result: v}))
      })
    })
  }

  public rmFile(root: string, file: string): Promise<void> {
    return new Promise(async (resolve, reject) => {
      const rmRoot: string = path.join(root, file)
      if (this.checkFileIsExists(rmRoot)) {
        try {
          await fs.unlinkSync(rmRoot)
          resolve()
        } catch (e) {
          reject(e)
        }
      }
      reject()
    })
  }

  public async rmDir(root: string): Promise<void> {
    await fs.readdirSync(root).map(v => {
      let curPath = path.join(root, v)
      if (fs.statSync(curPath).isDirectory()) {
        this.rmDir(curPath)
      } else {
        this.rmFile(root, v)
      }
    })
    fs.rmdirSync(root)
  }

  public async trash(root): Promise<void> {
    await trash(root)
  }


}
