import cs from 'classnames'

import { action, computed, observable } from 'mobx'
import { observer } from 'mobx-react'
import React, { Component, createRef } from 'react'
import Tooltip from '@cypress/react-tooltip'
import { $ } from '@packages/driver'

import { ViewportInfo } from '../viewport-info'
import { SelectorPlayground } from '../selector-playground/SelectorPlayground'
import { selectorPlaygroundModel } from '../selector-playground'
import { Studio, studioRecorder } from '../studio'
import { eventManager } from '../event-manager'

interface BaseState {
  isLoading: boolean
  isRunning: boolean
  width: number
  height: number
  displayScale: number | undefined
  defaults: {
    width: number
    height: number
  }
  updateWindowDimensions: (payload: { headerHeight: number }) => void
}

interface StateCT {
  runner: 'component'
  state: {
    screenshotting: boolean
  } & BaseState
}

interface StateE2E {
  runner: 'e2e'
  state: {
    url: string
    isLoadingUrl: boolean
    highlightUrl: boolean
  } & BaseState
}

interface HeaderBaseProps {
  config: {
    configFile: string
    [k: string]: unknown
  }
}

type CtHeaderProps = StateCT & HeaderBaseProps

type E2EHeaderProps = StateE2E & HeaderBaseProps

type HeaderProps = CtHeaderProps | E2EHeaderProps

@observer
export class Header extends Component<HeaderProps> {
  @observable showingViewportMenu = false
  @observable urlInput = ''
  @observable previousSelectorPlaygroundOpen: boolean = false
  @observable previousRecorderIsOpen: boolean = false

  urlInputRef = createRef<HTMLInputElement>()
  headerRef = createRef<HTMLHeadElement>()

  get studioForm () {
    if (this.props.runner !== 'e2e') {
      return
    }

    return (
      <form
        className={cs('url-container', {
          loading: this.props.runner === 'e2e' && this.props.state.isLoadingUrl,
          highlighted: this.props.runner === 'e2e' && this.props.state.highlightUrl,
          'menu-open': this._studioNeedsUrl,
        })}
        onSubmit={this._visitUrlInput}
      >
        <input
          ref={this.urlInputRef}
          type='text'
          className={cs('url', { 'input-active': this._studioNeedsUrl })}
          value={this._studioNeedsUrl ? this.urlInput : this.props.state.url}
          readOnly={!this._studioNeedsUrl}
          onChange={this._onUrlInput}
          onClick={this._openUrl}
        />
        <div className='popup-menu url-menu'>
          <p>
            <strong>Please enter a valid URL to visit.</strong>
          </p>
          <div className='menu-buttons'>
            <button type='button' className='btn-cancel' onClick={this._cancelStudio}>Cancel</button>
            <button type='submit' className='btn-submit' disabled={!this.urlInput}>
              {`Go `}
              <i className='fas fa-arrow-right' />
            </button>
          </div>
        </div>
        <span className='loading-container'>
          ...loading
          {' '}
          <i className='fas fa-spinner fa-pulse' />
        </span>
      </form>
    )
  }

  render () {
    const { config, state } = this.props

    return (
      <header
        ref={this.headerRef}
        className={cs({
          'showing-selector-playground': selectorPlaygroundModel.isOpen,
          'showing-studio': studioRecorder.isOpen,
          'display-none': this.props.runner === 'component' && this.props.state.screenshotting,
        })}
      >
        <div className='sel-url-wrap'>
          <Tooltip
            title='Open Selector Playground'
            visible={selectorPlaygroundModel.isOpen || studioRecorder.isOpen ? false : null}
            wrapperClassName='selector-playground-toggle-tooltip-wrapper'
            className='cy-tooltip'
          >
            <button
              aria-label='Open Selector Playground'
              className='header-button selector-playground-toggle'
              disabled={this.props.state.isLoading || state.isRunning || studioRecorder.isOpen}
              onClick={this._togglePlaygroundOpen}
            >
              <i aria-hidden="true" className='fas fa-crosshairs' />
            </button>
          </Tooltip>
          <div className={cs('menu-cover', { 'menu-cover-display': this._studioNeedsUrl })} />
          {this.studioForm}
        </div>

        <ViewportInfo
          showingViewportMenu={this.showingViewportMenu}
          width={state.width}
          height={state.height}
          config={config}
          displayScale={this.props.runner === 'e2e' ? state.displayScale : undefined}
          defaults={{
            width: state.defaults.width,
            height: state.defaults.height,
          }}
          toggleViewportMenu={this._toggleViewportMenu}
        />

        <SelectorPlayground
          model={selectorPlaygroundModel}
          eventManager={eventManager}
        />
        {this.props.runner === 'e2e' &&
          <Studio model={studioRecorder} hasUrl={!!this.props.state.url} />}
      </header>
    )
  }

  @action componentDidMount () {
    this.previousSelectorPlaygroundOpen = selectorPlaygroundModel.isOpen
    this.previousRecorderIsOpen = studioRecorder.isOpen

    this.urlInput = this.props.config.baseUrl ? `${this.props.config.baseUrl}/` : ''
  }

  @action componentDidUpdate () {
    if (selectorPlaygroundModel.isOpen !== this.previousSelectorPlaygroundOpen) {
      this._updateWindowDimensions()
      this.previousSelectorPlaygroundOpen = selectorPlaygroundModel.isOpen
    }

    if (studioRecorder.isOpen !== this.previousRecorderIsOpen) {
      this._updateWindowDimensions()
      this.previousRecorderIsOpen = studioRecorder.isOpen
    }

    if (this._studioNeedsUrl && this.urlInputRef.current) {
      this.urlInputRef.current.focus()
    }
  }

  _togglePlaygroundOpen = () => {
    selectorPlaygroundModel.toggleOpen()
  }

  @action _toggleViewportMenu = () => {
    this.showingViewportMenu = !this.showingViewportMenu
  }

  _updateWindowDimensions = () => {
    if (!this.headerRef.current) {
      return
    }

    this.props.state.updateWindowDimensions({
      headerHeight: $(this.headerRef.current).outerHeight(),
    })
  }

  _openUrl = () => {
    if (this._studioNeedsUrl || this.props.runner !== 'e2e') {
      return
    }

    window.open(this.props.state.url)
  }

  @computed get _studioNeedsUrl () {
    if (this.props.runner !== 'e2e') {
      return
    }

    return studioRecorder.needsUrl && !this.props.state.url
  }

  @action _onUrlInput = (e) => { // : React.FormEvent<HTMLInputElement>) => {
    if (!this._studioNeedsUrl) return

    this.urlInput = e.target.value
  }

  @action _visitUrlInput = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    if (!this._studioNeedsUrl) return

    studioRecorder.visitUrl(this.urlInput)

    this.urlInput = ''
  }

  _cancelStudio = () => {
    eventManager.emit('studio:cancel')
  }
}
