import React from 'react'
import { invoke } from '@tauri-apps/api'
import { dataDir } from '@tauri-apps/api/path'
import DirInput from '../common/DirInput'
import Menu from './Menu'
import Tr, { getLanguages } from '../../../utils/language'
import { setConfigOption, getConfig, getConfigOption, Configuration } from '../../../utils/configuration'
import Checkbox from '../common/Checkbox'
import Divider from './Divider'
import { getThemeList } from '../../../utils/themes'
import * as server from '../../../utils/server'

import './Options.css'
import BigButton from '../common/BigButton'
import DownloadHandler from '../../../utils/download'
import * as meta from '../../../utils/rsa'
import HelpButton from '../common/HelpButton'
import SmallButton from '../common/SmallButton'

export enum GrasscutterElevation {
  None = 'None',
  Capability = 'Capability',
  Root = 'Root',
}

interface IProps {
  closeFn: () => void
  downloadManager: DownloadHandler
}

interface IState {
  game_install_path: string
  grasscutter_path: string
  java_path: string
  grasscutter_with_game: boolean
  language_options: { [key: string]: string }[]
  current_language: string
  bg_url_or_path: string
  themes: string[]
  theme: string
  use_theme_background: boolean
  encryption: boolean
  patch_rsa: boolean
  use_internal_proxy: boolean
  wipe_login: boolean
  horny_mode: boolean
  auto_mongodb: boolean
  swag: boolean
  platform: string
  un_elevated: boolean
  redirect_more: boolean

  // Linux stuff
  grasscutter_elevation: string

  // Swag stuff
  akebi_path: string
  migoto_path: string
  reshade_path: string
}

export default class Options extends React.Component<IProps, IState> {
  constructor(props: IProps) {
    super(props)

    this.state = {
      game_install_path: '',
      grasscutter_path: '',
      java_path: '',
      grasscutter_with_game: false,
      language_options: [],
      current_language: 'en',
      bg_url_or_path: '',
      themes: ['default'],
      theme: '',
      use_theme_background: false,
      encryption: false,
      patch_rsa: false,
      use_internal_proxy: false,
      wipe_login: false,
      horny_mode: false,
      swag: false,
      auto_mongodb: false,
      platform: '',
      un_elevated: false,
      redirect_more: false,

      // Linux stuff
      grasscutter_elevation: GrasscutterElevation.None,

      // Swag stuff
      akebi_path: '',
      migoto_path: '',
      reshade_path: '',
    }

    this.setGameExecutable = this.setGameExecutable.bind(this)
    this.setGrasscutterJar = this.setGrasscutterJar.bind(this)
    this.setJavaPath = this.setJavaPath.bind(this)
    this.setAkebi = this.setAkebi.bind(this)
    this.setMigoto = this.setMigoto.bind(this)
    this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
    this.setCustomBackground = this.setCustomBackground.bind(this)
    this.toggleEncryption = this.toggleEncryption.bind(this)
    this.removeRSA = this.removeRSA.bind(this)
    this.addMigotoDelay = this.addMigotoDelay.bind(this)
    this.toggleUnElevatedGame = this.toggleUnElevatedGame.bind(this)
  }

  async componentDidMount() {
    const config = await getConfig()
    const languages = await getLanguages()
    const platform: string = await invoke('get_platform')

    let encEnabled
    if (config.grasscutter_path) {
      // Remove jar from path
      const path = config.grasscutter_path.replace(/\\/g, '/')
      const folderPath = path.substring(0, path.lastIndexOf('/'))
      encEnabled = await server.encryptionEnabled(folderPath + '/config.json')
    }

    console.log(platform)

    this.setState({
      game_install_path: config.game_install_path || '',
      grasscutter_path: config.grasscutter_path || '',
      java_path: config.java_path || '',
      grasscutter_with_game: config.grasscutter_with_game || false,
      language_options: languages,
      current_language: config.language || 'en',
      bg_url_or_path: config.custom_background || '',
      themes: (await getThemeList()).map((t) => t.name),
      theme: config.theme || 'default',
      use_theme_background: config.use_theme_background || false,
      encryption: encEnabled || false,
      patch_rsa: config.patch_rsa || false,
      use_internal_proxy: config.use_internal_proxy || false,
      wipe_login: config.wipe_login || false,
      horny_mode: config.horny_mode || false,
      swag: config.swag_mode || false,
      auto_mongodb: config.auto_mongodb || false,
      platform,
      un_elevated: config.un_elevated || false,
      redirect_more: config.redirect_more || false,

      // Linux stuff
      grasscutter_elevation: config.grasscutter_elevation || GrasscutterElevation.None,

      // Swag stuff
      akebi_path: config.akebi_path || '',
      migoto_path: config.migoto_path || '',
      reshade_path: config.reshade_path || '',
    })

    this.forceUpdate()
  }

  setGameExecutable(value: string) {
    setConfigOption('game_install_path', value)

    // I hope this stops people setting launcher.exe because oml it's annoying
    if (value.endsWith('launcher.exe')) {
      const pathArr = value.replace(/\\/g, '/').split('/')
      pathArr.pop()
      const path = pathArr.join('/') + '/Genshin Impact Game/'

      alert(
        `You have set your game execuatable to "launcher.exe". You should not do this. Your game executable is located in:\n\n${path}`
      )
    }

    // If setting any other game, automatically set to redirect more
    if (!value.toLowerCase().includes('genshin' || 'yuanshen')) {
      if (!this.state.redirect_more) {
        this.toggleOption('redirect_more')
      }
    }

    this.setState({
      game_install_path: value,
    })
  }

  async setGrasscutterJar(value: string) {
    setConfigOption('grasscutter_path', value)

    this.setState({
      grasscutter_path: value,
    })

    const config = await getConfig()
    const path = config.grasscutter_path.replace(/\\/g, '/')
    const folderPath = path.substring(0, path.lastIndexOf('/'))
    const encEnabled = await server.encryptionEnabled(folderPath + '/config.json')

    // Update encryption button when setting new jar
    this.setState({
      encryption: encEnabled,
    })

    window.location.reload()
  }

  setJavaPath(value: string) {
    setConfigOption('java_path', value)

    this.setState({
      java_path: value,
    })
  }

  setAkebi(value: string) {
    setConfigOption('akebi_path', value)

    this.setState({
      akebi_path: value,
    })
  }

  setMigoto(value: string) {
    setConfigOption('migoto_path', value)

    this.setState({
      migoto_path: value,
    })
  }

  setReshade(value: string) {
    setConfigOption('reshade_path', value)

    this.setState({
      reshade_path: value,
    })
  }

  async setLanguage(value: string) {
    await setConfigOption('language', value)
    window.location.reload()
  }

  async setTheme(value: string) {
    await setConfigOption('theme', value)
    window.location.reload()
  }

  async toggleGrasscutterWithGame() {
    const changedVal = !(await getConfigOption('grasscutter_with_game'))
    setConfigOption('grasscutter_with_game', changedVal)

    this.setState({
      grasscutter_with_game: changedVal,
    })
  }

  async setCustomBackground(value: string) {
    const isUrl = /^(?:http(s)?:\/\/)/gm.test(value)

    if (!value) return await setConfigOption('custom_background', '')

    if (!isUrl) {
      const filename = value.replace(/\\/g, '/').split('/').pop()
      const localBgPath = (await dataDir()).replace(/\\/g, '/')

      await setConfigOption('custom_background', `${localBgPath}/cultivation/bg/${filename}`)

      // Copy the file over to the local directory
      await invoke('copy_file', {
        path: value.replace(/\\/g, '/'),
        newPath: `${localBgPath}cultivation/bg/`,
      })

      window.location.reload()
    } else {
      await setConfigOption('custom_background', value)
      window.location.reload()
    }
  }

  async toggleEncryption() {
    const config = await getConfig()

    // Check if grasscutter path is set
    if (!config.grasscutter_path) {
      alert('Grasscutter not set!')
      return
    }

    // Remove jar from path
    const path = config.grasscutter_path.replace(/\\/g, '/')
    const folderPath = path.substring(0, path.lastIndexOf('/'))

    await server.toggleEncryption(folderPath + '/config.json')

    this.setState({
      encryption: await server.encryptionEnabled(folderPath + '/config.json'),
    })

    // Check if Grasscutter is running, and restart if so to apply changes
    if (await invoke('is_grasscutter_running')) {
      alert('Automatically restarting Grasscutter to apply encryption changes!')
      await invoke('restart_grasscutter')
    }
  }

  async toggleUnElevatedGame() {
    const changedVal = !(await getConfigOption('un_elevated'))
    setConfigOption('un_elevated', changedVal)

    this.setState({
      un_elevated: changedVal,
    })
  }

  async setGCElevation(value: string) {
    setConfigOption('grasscutter_elevation', value)

    this.setState({
      grasscutter_elevation: value,
    })
  }

  async removeRSA() {
    await meta.unpatchGame()
  }

  async addMigotoDelay() {
    invoke('set_migoto_delay', {
      migotoPath: this.state.migoto_path,
    })
  }

  async installCert() {
    await invoke('generate_ca_files', {
      path: (await dataDir()) + 'cultivation',
    })
  }

  async toggleOption(opt: keyof Configuration) {
    const changedVal = !(await getConfigOption(opt))

    await setConfigOption(opt, changedVal)

    // @ts-expect-error shut up bitch
    this.setState({
      [opt]: changedVal,
    })
  }

  render() {
    return (
      <Menu closeFn={this.props.closeFn} className="Options" heading="Options">
        <div className="OptionSection" id="menuOptionsContainerGamePath">
          <div className="OptionLabel" id="menuOptionsLabelGamePath">
            <Tr text="options.game_path" />
          </div>
          <div className="OptionValue" id="menuOptionsDirGamePath">
            <DirInput onChange={this.setGameExecutable} value={this.state?.game_install_path} extensions={['exe']} />
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainermetaDownload">
          <div className="OptionLabel" id="menuOptionsLabelmetaDownload">
            <Tr text="options.recover_rsa" />
            <HelpButton contents="help.emergency_rsa" />
          </div>
          <div className="OptionValue" id="menuOptionsButtonmetaDownload">
            <BigButton onClick={this.removeRSA} id="metaDownload">
              <Tr text="components.delete" />
            </BigButton>
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainerPatchMeta">
          <div className="OptionLabel" id="menuOptionsLabelPatchMeta">
            <Tr text="options.patch_rsa" />
            <HelpButton contents="help.patch_rsa" />
          </div>
          <div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
            <Checkbox onChange={() => this.toggleOption('patch_rsa')} checked={this.state?.patch_rsa} id="patchMeta" />
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainerUseProxy">
          <div className="OptionLabel" id="menuOptionsLabelUseProxy">
            <Tr text="options.use_proxy" />
            <HelpButton contents="help.use_proxy" />
          </div>
          <div className="OptionValue" id="menuOptionsCheckboxUseProxy">
            <Checkbox
              onChange={() => this.toggleOption('use_internal_proxy')}
              checked={this.state?.use_internal_proxy}
              id="useProxy"
            />
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainerWipeLogin">
          <div className="OptionLabel" id="menuOptionsLabelWipeLogin">
            <Tr text="options.wipe_login" />
          </div>
          <div className="OptionValue" id="menuOptionsCheckboxWipeLogin">
            <Checkbox
              onChange={() => this.toggleOption('wipe_login')}
              checked={this.state?.wipe_login}
              id="wipeLogin"
            />
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainerAutoMongodb">
          <div className="OptionLabel" id="menuOptionsLabelAutoMongodb">
            <Tr text="options.auto_mongodb" />
          </div>
          <div className="OptionValue" id="menuOptionsCheckboxAutoMongodb">
            <Checkbox
              onChange={() => this.toggleOption('auto_mongodb')}
              checked={this.state?.auto_mongodb}
              id="autoMongodb"
            />
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainerRedirect">
          <div className="OptionLabel" id="menuOptionsLabelRedirect">
            <Tr text="options.redirect_more" />
          </div>
          <div className="OptionValue" id="menuOptionsCheckboxRedirect">
            <Checkbox
              onChange={() => this.toggleOption('redirect_more')}
              checked={this.state?.redirect_more}
              id="RedirectMore"
            />
          </div>
        </div>

        <Divider />

        <div className="OptionSection" id="menuOptionsContainerGCJar">
          <div className="OptionLabel" id="menuOptionsLabelGCJar">
            <Tr text="options.grasscutter_jar" />
          </div>
          <div className="OptionValue" id="menuOptionsDirGCJar">
            <DirInput onChange={this.setGrasscutterJar} value={this.state?.grasscutter_path} extensions={['jar']} />
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainerToggleEnc">
          <div className="OptionLabel" id="menuOptionsLabelToggleEnc">
            <Tr text="options.toggle_encryption" />
            <HelpButton contents="help.encryption" />
          </div>
          <div className="OptionValue" id="menuOptionsButtonToggleEnc">
            <Checkbox onChange={() => this.toggleEncryption()} checked={this.state.encryption} id="toggleEnc" />
          </div>
        </div>
        <div className="OptionSection" id="menuOptionsContainerInstallCert">
          <div className="OptionLabel" id="menuOptionsLabelInstallCert">
            <Tr text="options.install_certificate" />
          </div>
          <div className="OptionValue" id="menuOptionsButtonInstallCert">
            <BigButton disabled={false} onClick={this.installCert} id="installCert">
              <Tr text="components.install" />
            </BigButton>
          </div>
        </div>
        {this.state.platform === 'linux' && (
          <>
            <Divider />
            <div className="OptionSection" id="menuOptionsContainerGCElevation">
              <div className="OptionLabel" id="menuOptionsLabelGCElevation">
                <Tr text="options.grasscutter_elevation" />
                <HelpButton contents="help.grasscutter_elevation_help_text" />
              </div>
              <select
                value={this.state.grasscutter_elevation}
                id="menuOptionsSelectGCElevation"
                onChange={(event) => {
                  this.setGCElevation(event.target.value)
                }}
              >
                {Object.keys(GrasscutterElevation).map((t) => (
                  <option key={t} value={t}>
                    {t}
                  </option>
                ))}
              </select>
            </div>
            <div className="OptionSection" id="menuOptionsContainerCheckAAGL">
              <div className="OptionLabel" id="menuOptionsLabelCheckAAGL">
                <Tr text="options.check_aagl" />
              </div>
            </div>
          </>
        )}
        {this.state.swag && (
          <>
            <Divider />
            <div className="OptionSection" id="menuOptionsContainerAkebi">
              <div className="OptionLabel" id="menuOptionsLabelAkebi">
                <Tr text="swag.akebi" />
              </div>
              <div className="OptionValue" id="menuOptionsDirAkebi">
                <DirInput onChange={this.setAkebi} value={this.state?.akebi_path} extensions={['exe']} />
              </div>
            </div>
            <div className="OptionSection" id="menuOptionsContainerMigoto">
              <div className="OptionLabel" id="menuOptionsLabelMigoto">
                <Tr text="swag.migoto" />
              </div>
              <div className="OptionValue" id="menuOptionsDirMigoto">
                <SmallButton onClick={this.addMigotoDelay} id="migotoDelay" contents="help.add_delay"></SmallButton>
                <DirInput onChange={this.setMigoto} value={this.state?.migoto_path} extensions={['exe']} />
              </div>
            </div>
            <div className="OptionSection" id="menuOptionsContainerReshade">
              <div className="OptionLabel" id="menuOptionsLabelReshade">
                <Tr text="swag.reshade" />
              </div>
              <div className="OptionValue" id="menuOptionsDirReshade">
                <DirInput onChange={this.setReshade} value={this.state?.reshade_path} extensions={['exe']} />
              </div>
            </div>
          </>
        )}

        <Divider />

        <div className="OptionSection" id="menuOptionsContainerGCWGame">
          <div className="OptionLabel" id="menuOptionsLabelGCWDame">
            <Tr text="options.grasscutter_with_game" />
          </div>
          <div className="OptionValue" id="menuOptionsCheckboxGCWGame">
            <Checkbox
              onChange={() => this.toggleOption('grasscutter_with_game')}
              checked={this.state?.grasscutter_with_game}
              id="gcWithGame"
            />
          </div>
        </div>
        {this.state.platform !== 'linux' && (
          <div className="OptionSection" id="menuOptionsContainerUEGame">
            <div className="OptionLabel" id="menuOptionsLabelUEGame">
              <Tr text="options.un_elevated" />
            </div>
            <div className="OptionValue" id="menuOptionsCheckboxUEGame">
              <Checkbox
                onChange={() => this.toggleOption('un_elevated')}
                checked={this.state?.un_elevated}
                id="unElevatedGame"
              />
            </div>
          </div>
        )}
        {this.state.swag ? (
          <div className="OptionSection" id="menuOptionsContainerHorny">
            <div className="OptionLabel" id="menuOptionsLabelHorny">
              <Tr text="options.horny_mode" />
            </div>
            <div className="OptionValue" id="menuOptionsCheckboxHorny">
              <Checkbox
                onChange={() => this.toggleOption('horny_mode')}
                checked={this.state?.horny_mode}
                id="hornyMode"
              />
            </div>
          </div>
        ) : null}

        <Divider />

        <div className="OptionSection" id="menuOptionsContainerThemes">
          <div className="OptionLabel" id="menuOptionsLabelThemes">
            <Tr text="options.theme" />
          </div>
          <div className="OptionValue" id="menuOptionsSelectThemes">
            <select
              value={this.state.theme}
              id="menuOptionsSelectMenuThemes"
              onChange={(event) => {
                this.setTheme(event.target.value)
              }}
            >
              {this.state.themes.map((t) => (
                <option key={t} value={t}>
                  {t}
                </option>
              ))}
            </select>
          </div>
        </div>

        <div className="OptionSection" id="menuOptionsContainerUseThemeBG">
          <div className="OptionLabel" id="menuOptionsLabelUseThemeBG">
            <Tr text="options.use_theme_background" />
          </div>
          <div className="OptionValue" id="menuOptionsUseThemeBG">
            <Checkbox
              onChange={() => this.toggleOption('use_theme_background')}
              checked={this.state?.use_theme_background}
              id="useThemeBG"
            />
          </div>
        </div>

        <Divider />

        <div className="OptionSection" id="menuOptionsContainerJavaPath">
          <div className="OptionLabel" id="menuOptionsLabelJavaPath">
            <Tr text="options.java_path" />
          </div>
          <div className="OptionValue" id="menuOptionsDirJavaPath">
            <DirInput onChange={this.setJavaPath} value={this.state?.java_path} extensions={['exe']} />
          </div>
        </div>

        <div className="OptionSection" id="menuOptionsContainerBG">
          <div className="OptionLabel" id="menuOptionsLabelBG">
            <Tr text="options.background" />
          </div>
          <div className="OptionValue" id="menuOptionsDirBG">
            <DirInput
              onChange={this.setCustomBackground}
              value={this.state?.bg_url_or_path}
              extensions={['png', 'jpg', 'jpeg']}
              readonly={false}
              clearable={true}
              customClearBehaviour={async () => {
                await setConfigOption('custom_background', '')
                window.location.reload()
              }}
            />
          </div>
        </div>

        <div className="OptionSection" id="menuOptionsContainerLang">
          <div className="OptionLabel" id="menuOptionsLabelLang">
            <Tr text="options.language" />
          </div>
          <div className="OptionValue" id="menuOptionsSelectLang">
            <select
              value={this.state.current_language}
              id="menuOptionsSelectMenuLang"
              onChange={(event) => {
                this.setLanguage(event.target.value)
              }}
            >
              {this.state.language_options.map((lang) => (
                <option key={Object.keys(lang)[0]} value={Object.keys(lang)[0]}>
                  {lang[Object.keys(lang)[0]]}
                </option>
              ))}
            </select>
          </div>
        </div>
      </Menu>
    )
  }
}
