import type { AllAppModule } from '@/core/type'
import localForage from 'localforage'
import { SetupStore } from '@/components/Setup/store'
import { MessageError } from '@/util/Element'
import { I18nT } from '@lang/index'
import { fs, shell } from '@/util/NodeFn'
import { reactiveBind } from '@/util/Index'
import IPC from '@/util/IPC'
import Base from '@/core/Base'
import { join } from '@/util/path-browserify'
import { ProjectItem } from './ProjectItem'
import { AsyncComponentShow } from '@/util/AsyncComponent'

export class Project {
  allDirs: string[] = []
  fetching = false
  project: ProjectItem[] = []
  search = ''
  flagType: AllAppModule = 'golang'

  constructor(flagType: AllAppModule) {
    this.flagType = flagType
  }

  action(item: ProjectItem, index: number, action: 'open' | 'edit' | 'log' | 'config') {
    switch (action) {
      case 'open':
        shell.openPath(item.path).catch()
        break
      case 'edit':
        import('./ProjectEdit.vue').then((res) => {
          AsyncComponentShow(res.default, {
            isEdit: true,
            edit: item,
            typeFlag: this.flagType
          }).then((res: ProjectItem) => {
            if (res) {
              console.log('action: ', item)
              const isRun = item?.state?.isRun
              item
                .stop()
                .catch()
                .finally(() => {
                  const state = JSON.parse(JSON.stringify(item.state))
                  Object.assign(item, res)
                  Object.assign(item.state, state)
                  this.saveProject()
                  this.setDirEnv(item).catch()
                  if (isRun) {
                    item.start().catch()
                  }
                })
            }
          })
        })
        break
      case 'log':
        import('./LogViewer.vue').then((res) => {
          AsyncComponentShow(res.default, {
            item
          }).catch()
        })
        break
      case 'config':
        import('./ConfigViewer.vue').then((res) => {
          AsyncComponentShow(res.default, {
            item
          }).catch()
        })
        break
    }
  }

  saveProject() {
    localForage
      .setItem(`flyenv-${this.flagType}-projects`, JSON.parse(JSON.stringify(this.project)))
      .then()
      .catch()
  }
  fetchProject() {
    if (this.fetching) {
      return
    }
    this.fetching = true
    localForage
      .getItem(`flyenv-${this.flagType}-projects`)
      .then((res: ProjectItem[]) => {
        if (res) {
          this.project.splice(0)
          for (const i of res) {
            const item = reactiveBind(new ProjectItem({ ...i, typeFlag: this.flagType }))
            this.project.push(item)
          }
        }
      })
      .catch()
      .finally(() => {
        this.fetching = false
      })
  }
  addProject() {
    const setupStore = SetupStore()
    const isLock = !setupStore.isActive && this.project.length > 2
    if (isLock) {
      MessageError(I18nT('host.licenseTips'))
      return
    }
    import('./ProjectEdit.vue').then((res) => {
      AsyncComponentShow(res.default, {
        isEdit: false,
        edit: {},
        typeFlag: this.flagType
      }).then((res: ProjectItem) => {
        if (res) {
          const item = reactiveBind(new ProjectItem({ ...res, typeFlag: this.flagType }))
          this.project.unshift(item)
          this.saveProject()
          this.setDirEnv(item).catch()
        }
      })
    })
  }
  initDirs() {
    localForage
      .getItem('flyenv-projects-dirs')
      .then((res: string[]) => {
        if (res) {
          this.allDirs = res
          const dirs = window.Server.isWindows ? JSON.stringify(res) : res.join('\n')
          IPC.send('app-fork:tools', 'initAllowDir', dirs).then((key: string) => {
            IPC.off(key)
          })
        }
      })
      .catch(() => {
        this.allDirs = []
      })
  }
  saveDirs(): Promise<any> {
    return localForage.setItem('flyenv-projects-dirs', JSON.parse(JSON.stringify(this.allDirs)))
  }
  delProject(index: number) {
    Base._Confirm(I18nT('base.delAlertContent'), undefined, {
      customClass: 'confirm-del',
      type: 'warning'
    })
      .then(() => {
        const item = this.project[index]
        item.stop().catch()
        this.project.splice(index, 1)
        this.saveProject()
        const dirIndex = this.allDirs.indexOf(item.path)
        if (dirIndex >= 0) {
          this.allDirs.splice(dirIndex, 1)
        }
        this.saveDirs().then(() => {
          this.initDirs()
        })
      })
      .catch(() => {})
  }
  async setDirEnv(item: ProjectItem) {
    IPC.send('app-fork:tools', 'initFlyEnvSH').then((key: string, res: any) => {
      IPC.off(key)
      if (res?.code === 1) {
        MessageError(res?.msg ?? '')
      }
    })
    if (!this.allDirs.includes(item.path)) {
      this.allDirs.push(item.path)
    }
    this.saveDirs().then(() => {
      this.initDirs()
    })
    if (window.Server.isWindows) {
      try {
        const envFile = join(item.path, '.flyenv')
        const exists = await fs.existsSync(envFile)
        if (!exists) {
          if (!item.binVersion) {
            await fs.writeFile(envFile, '')
          } else {
            const arr: string[] = []
            const list = [item.binPath, join(item.binPath, 'bin'), join(item.binPath, 'sbin')]
            for (const s of list) {
              const e = await fs.existsSync(s)
              if (e) {
                arr.push(s)
              }
            }
            if (arr.length) {
              await fs.writeFile(
                envFile,
                `[Console]::OutputEncoding = [System.Text.Encoding]::UTF8\n$env:PATH = "${arr.join(';')};" + $env:PATH #FlyEnv-ID-${item.id}`
              )
            }
          }
        } else {
          const content = await fs.readFile(envFile)
          const lines = content
            .trim()
            .split('\n')
            .filter((s: string) => {
              const line = s.trim()
              return !!line && !line.includes(`#FlyEnv-ID-${item.id}`)
            })
          if (item.binVersion) {
            const arr: string[] = []
            const list = [item.binPath, join(item.binPath, 'bin'), join(item.binPath, 'sbin')]
            for (const s of list) {
              const e = await fs.existsSync(s)
              if (e) {
                arr.push(s)
              }
            }
            if (arr.length) {
              lines.push(`$env:PATH = "${arr.join(';')};" + $env:PATH #FlyEnv-ID-${item.id}`)
            }
          }
          await fs.writeFile(envFile, lines.join('\n'))
        }
      } catch (e: any) {
        MessageError(e.toString())
      }
    } else {
      try {
        const envFile = join(item.path, '.flyenv')
        const exists = await fs.existsSync(envFile)
        if (!exists) {
          if (!item.binVersion) {
            await fs.writeFile(envFile, '')
          } else {
            const arr: string[] = []
            const list = [item.binPath, join(item.binPath, 'bin'), join(item.binPath, 'sbin')]
            for (const s of list) {
              const e = await fs.existsSync(s)
              if (e) {
                arr.push(s)
              }
            }
            if (arr.length) {
              await fs.writeFile(
                envFile,
                `#!/bin/zsh\nexport PATH="${arr.join(':')}:$PATH" #FlyEnv-ID-${item.id}`
              )
            }
          }
        } else {
          const content = await fs.readFile(envFile)
          const lines = content
            .trim()
            .split('\n')
            .filter((s: string) => {
              const line = s.trim()
              return !!line && !line.includes(`#FlyEnv-ID-${item.id}`)
            })
          if (item.binVersion) {
            const arr: string[] = []
            const list = [item.binPath, join(item.binPath, 'bin'), join(item.binPath, 'sbin')]
            for (const s of list) {
              const e = await fs.existsSync(s)
              if (e) {
                arr.push(s)
              }
            }
            if (arr.length) {
              lines.push(`export PATH="${arr.join(':')}:$PATH" #FlyEnv-ID-${item.id}`)
            }
          }
          await fs.writeFile(envFile, lines.join('\n'))
        }
      } catch (e: any) {
        MessageError(e.toString())
      }
    }
  }
  stopAll(): Promise<boolean[]> {
    return Promise.all(this.project.map((p) => p.stop()))
  }
  startAll(): Promise<Array<string | boolean>> {
    return Promise.all(this.project.map((p) => p.start()))
  }
}
