import { ApplicationProptey } from './proptey'
import { Module, ModuleManifest } from '../types'
import { BASE_CSS } from './init-css'

interface AppRoute {
  id: string
  param: string
  search: string
}

class ApplicationBase extends ApplicationProptey {
  constructor () {
    super()
    this.setBaseCSS()
  }

  get route (): AppRoute {
    const router = this.routerRegExp.exec(location.hash) || []
    const id = router[1]
    const param = router[2]
    const search = location.search

    return {
      id: id ? decodeURIComponent(id) : '',
      param: param ? decodeURIComponent(param) : '',
      search
    }
  }

  public properties = {
    darkTheme: window.matchMedia?.('(prefers-color-scheme: dark)')?.matches
  }

  public setBaseCSS () {
    const style = document.createElement('style')
    style.innerHTML = BASE_CSS
    document.getElementsByTagName('head')[0].appendChild(style)
  }

  public resolveURL (url: string) {
    const link = document.createElement('a')
    link.href = url
    const path = link.origin + link.pathname
    return {
      path,
      origin: link.origin,
      host: link.host,
      search: link.search
    }
  }

  public promiseModule (promise: () => Promise<ModuleManifest>): Promise<ModuleManifest> {
    return new Promise(async (resolve, reject) => {
      try {
        resolve(await promise())
      } catch (error) {
        reject(error)
      }
    })
  }

  public moduleSrcVerify (url: string): boolean {
    const allowHosts = this.config.allowHosts
    if (!Array.isArray(allowHosts)) {
      this.console.warn('Module.config.allowHosts is not defined', 'Security risks exist: ', '')
      return true
    }
    const link = document.createElement('a')
    link.href = decodeURIComponent(url)
    const linkHost = link.host
    for (const host of allowHosts) {
      if (linkHost === host) return true
    }
    return false
  }

  public getModuleByURL (url: string): Module | undefined {
    const modules = this.modules
    const resolve = this.resolveURL(url)
    const path = resolve.path
    for (const id in modules) {
      const module = modules[id]
      const capture = module.config.capture
      if (typeof capture === 'string') {
        if (capture === path) return module
      } else if (typeof capture === 'function') {
        if (capture(resolve, url)) return module
      }
      if (module.uri === path) return module
    }
    return
  }

  public setExists () {
    return new Promise<void>((resolve, reject) => {
      try {
        sessionStorage.setItem('__EXISTS', String(history.length))
        resolve()
      } catch (e) {
        reject()
      }
    })
  }

  get exists (): boolean {
    try {
      return parseInt(sessionStorage.getItem('__EXISTS') || '-1', 10) === history.length
    } catch (e) {
      return false
    }
  }

  public readonly console = {
    echo: (type: 'log' | 'info' | 'warn' | 'error', pre: string[], mid: string[], suf: string[]) => {
      console[type](
        '%c ' +
        (pre[0] ? pre[0] + ' ' : '') +
        '%c ' + (mid[0] ? mid[0] + ' ' : '') +
        '%c ' + (suf[0] ? suf[0] + ' ' : ''),
        'color: #ffffff; background:' + pre[1],
        'color: #ffffff; background:' + mid[1],
        'color: #ffffff; background:' + (suf[0] ? suf[1] : mid[1])
      )
    },
    log: (message: string, title: string, description: string) => {
      this.console.echo('log', [title, '#999'], [message, '#333'], [description, '#666'])
    },
    info: (message: string, title: string, description: string) => {
      this.console.echo('info', [title, '#0cf'], [message, '#06c'], [description, '#0c0'])
    },
    warn: (message: string, title: string, description: string) => {
      this.console.echo('warn', [title, '#f60'], [message, '#f30'], [description, '#f90'])
    },
    error: (message: string, title: string, description: string) => {
      this.console.echo('warn', [title, '#f06'], [message, '#903'], [description, '#993'])
    }
  }
}

export {
  ApplicationBase
}
