import * as compareVersions from 'compare-versions'
import * as electron from 'electron'
import * as os from 'os'
import * as React from 'react'
import axios from 'axios'
import Close from '@material-ui/icons/Close'
import CloudDownload from '@material-ui/icons/CloudDownload'
import { AppState } from '../reducers'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { green } from '@material-ui/core/colors'
import { Theme, withStyles } from '@material-ui/core/styles'
import { updateNotifierActions } from '../actions'

import { Button, IconButton, Modal, Paper, Snackbar, SnackbarContent, Typography } from '@material-ui/core'

interface Props {
  showUpdateNotification: boolean
  showUpdateDetails: boolean
  classes: any
  actions: any
}

interface GithubRelease {
  url: string
  assets?: Array<GithubAsset>
  published_at: string // "2019-01-25T20:14:39Z"
  body_html: string
  body: string
  body_text: string
  tag_name: string
  prerelease: boolean
}

interface GithubAsset {
  id: number
  node_id: string
  url: string
  browser_download_url: string
  name: string
  label: string
}

interface State {
  newerVersions: Array<GithubRelease>
}

class UpdateNotifier extends React.Component<Props, State> {
  constructor(props: any) {
    super(props)
    this.state = { newerVersions: [] }

    const ownVersion = electron.remote.app.getVersion()
    this.fetchReleases().then(releases => {
      const newerVersions = releases
        .filter(release => this.allowPrereleaseIfOwnVersionIsBeta(release, ownVersion))
        .filter(release => compareVersions(release.tag_name, ownVersion) > 0)
        .sort((a, b) => compareVersions(b.tag_name, a.tag_name))

      if (newerVersions.length > 0) {
        this.setState({ newerVersions })
        this.props.actions.showUpdateNotification(true)
      }
    })
  }

  private allowPrereleaseIfOwnVersionIsBeta(release: GithubRelease, ownVersion: string) {
    const ownVersionIsBeta = !/alpha|beta/.test(ownVersion)

    return ownVersionIsBeta || !release.prerelease
  }

  private async fetchReleases(): Promise<Array<GithubRelease>> {
    const res = await axios.get('https://api.github.com/repos/thomasnordquist/mqtt-explorer/releases', {
      headers: {
        accept: 'application/vnd.github.v3.full+json',
      },
    })

    return res.data as Array<GithubRelease>
  }

  private onCloseNotification = (event: React.SyntheticEvent<any>, reason: string) => {
    if (reason === 'clickaway') {
      return
    }
    this.props.actions.showUpdateNotification(false)
  }

  private closeNotification = () => {
    this.props.actions.showUpdateNotification(false)
  }

  private showDetails = () => {
    this.props.actions.showUpdateNotification(false)
    this.props.actions.showUpdateDetails(true)
  }

  private hideDetails = () => {
    this.props.actions.showUpdateDetails(false)
  }

  private renderUpdateNotification() {
    const snackbarAnchor: any = {
      vertical: 'top',
      horizontal: 'right',
    }

    return (
      <Snackbar
        anchorOrigin={snackbarAnchor}
        open={this.props.showUpdateNotification}
        autoHideDuration={12000}
        onClose={this.onCloseNotification}
      >
        <SnackbarContent
          className={this.props.classes.success}
          message={`${this.state.newerVersions.length} Update(s) available`}
          action={this.notificationActions()}
        />
      </Snackbar>
    )
  }

  private notificationActions() {
    return [
      <Button key="undo" size="small" onClick={this.showDetails}>
        Details
      </Button>,
      <IconButton
        key="close"
        aria-label="Close"
        color="inherit"
        className={this.props.classes.close}
        onClick={this.closeNotification}
      >
        <Close />
      </IconButton>,
    ]
  }

  private renderUpdateDetails() {
    const latestUpdate = this.state.newerVersions[0]
    if (!latestUpdate) {
      return null
    }
    const releaseNotes = this.state.newerVersions
      .map(release => `<p><h3>${release.tag_name}</h3><p/><p>${release.body_html}</p>`)
      .join('<hr />')

    return (
      <Modal open={this.props.showUpdateDetails} disableAutoFocus={true} onClose={this.hideDetails}>
        <Paper className={this.props.classes.root}>
          <Typography variant="h6" className={this.props.classes.title}>
            Version {latestUpdate.tag_name}
          </Typography>
          <Typography className={this.props.classes.title}>Changelog</Typography>
          <div className={this.props.classes.releaseNotes} dangerouslySetInnerHTML={{ __html: releaseNotes }} />
          {this.renderDownloads()}
          <Button className={this.props.classes.download} onClick={this.openHomePage}>
            Github Page
          </Button>
          <Button className={this.props.classes.closeButton} color="secondary" onClick={this.hideDetails}>
            Close
          </Button>
        </Paper>
      </Modal>
    )
  }

  private openHomePage = () => {
    this.openUrl('https://mqtt-explorer.com')
  }

  private openUrl = (url: string) => {
    electron.shell.openExternal(url)
  }

  private assetForCurrentPlatform(asset: GithubAsset) {
    let regex: RegExp
    if (os.platform() === 'darwin') {
      regex = /\.dmg$/
    } else if (os.platform() === 'win32') {
      regex = /\.exe$/
    } else {
      regex = /\.AppImage$/
    }

    return regex.test(asset.name)
  }

  private renderDownloads() {
    const latestUpdate = this.state.newerVersions[0]
    if (!latestUpdate || !latestUpdate.assets) {
      return null
    }

    return latestUpdate.assets.filter(this.assetForCurrentPlatform).map(asset => (
      <div>
        <Button className={this.props.classes.download} onClick={() => this.openUrl(asset.browser_download_url)}>
          <CloudDownload />
          &nbsp;{asset.name}
        </Button>
      </div>
    ))
  }

  public render() {
    return (
      <div>
        {this.renderUpdateNotification()}
        {this.renderUpdateDetails()}
      </div>
    )
  }
}

const styles = (theme: Theme) => ({
  success: {
    backgroundColor: green[600],
    color: theme.typography.button.color,
  },
  close: {
    padding: '4px',
  },
  root: {
    minWidth: '350px',
    maxWidth: '500px',
    backgroundColor: theme.palette.background.default,
    margin: '20vh auto auto auto',
    padding: theme.spacing(2),
    outline: 'none',
  },
  title: {
    color: theme.palette.text.primary,
  },
  releaseNotes: {
    overflow: 'auto scroll',
    color: theme.palette.text.secondary,
    backgroundColor: 'rgba(60, 60, 60, 0.6)',
    maxHeight: '28vh',
  },
  paper: {
    padding: theme.spacing(2),
    color: theme.palette.text.secondary,
  },
  download: {
    width: '100%',
  },
  closeButton: {
    display: 'block',
    margin: '0 0 0 auto',
  },
})

const mapStateToProps = (state: AppState) => {
  return {
    showUpdateNotification: state.globalState.get('showUpdateNotification'),
    showUpdateDetails: state.globalState.get('showUpdateDetails'),
  }
}

const mapDispatchToProps = (dispatch: any) => {
  return {
    actions: bindActionCreators(updateNotifierActions, dispatch),
  }
}

export default withStyles(styles)(
  connect(
    mapStateToProps,
    mapDispatchToProps
  )(UpdateNotifier)
)
