/*global document*/
import 'babel-polyfill'
import React from 'react'
import {render, findDOMNode} from 'react-dom'
import {Provider, connect} from 'react-redux'
import {createStore, combineReducers, compose} from 'redux'
import {BrowserRouter, Route} from 'react-router-dom'
import Editor from './editor'
import Preview from './preview'
import Controls from './controls'
import {doAction} from './api'
import Resources from './resources'
import '../reset.css'
import _ from 'lodash/fp'
import {Tab, Tabs, TabList, TabPanel} from 'react-tabs'
import uikitDependencies from '../ui-kit-dependencies.json'
import 'react-tabs/style/react-tabs.css'


const resources = (state = {}, action) => {
  switch(action.type) {
    case 'ADD_RESOURCE': {
      const {payload} = action
      return {...state, ...payload}
    }
    case 'REMOVE_RESOURCE': {
      const {ref} = action
      return _.omit(ref)(state)
    }
    default: return state
  }
}
const uiKit = (state = {}, action) => state
const uiKitStyles = (state = {}, action) => state

const rootReducer = combineReducers({
  resources,
  uiKit,
  uiKitStyles
})

const load = async () => {

  const editorModule = require('../weblium-editor.js').default
  const {_id} = await doAction('getBlock')
  const {data} = await doAction('resourceList', {ownerId: _id})
  const resources = data.reduce((acc, resource) => {
    const {alias, ...rest} = resource
    acc[resource.ref] = {...rest, ...alias}
    return acc
  }, {})

  // const uiKit = editorModule.uiKit.generateUIKit({
  //   metadata: {
  //     colorPalette: 'test'
  //   },
  //   colorPalette: {
  //     test: {
  //       name: 'Cli test pallete',
  //       type: 'universal',
  //       colors: {
  //         lightShadeColor: '#ffffff',
  //         lightAccentColor: '#cccccc',
  //         brandColor: '#909090',
  //         darkAccentColor: '#595959',
  //         darkShadeColor: '#242424',
  //       }
  //     }
  //   },
  //   uikitDependencies,
  // })
  // const uiKitStyles = editorModule.uiKit.generateThemesCss(uiKit) + '\n' + editorModule.commonUiKit

  const initialState = {
    resources,
    // uiKit,
    // uiKitStyles
  }

  const enhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || _.noop
  const store = createStore(rootReducer, initialState, enhancer())
  return {store}
}

@connect(_.pick('uiKitStyles'))
class App extends React.Component {
  componentWillMount() {
    doAction('getBlock')
      .then(({content, css, js, view, name, _id, categories, remote, published}) =>
        this.setState({
          remote,
          published,
          content,
          contentStr: JSON.stringify(content, null, 2),
          css, js, view, name, _id, categories, loading: false
        }))
      .catch(() => this.setState({error: {js: true}, loading: true}))
  }
  componentDidMount() {
    this.prependCss()
  }

  state = {
    error: {
      exists() {return this.js || this.content}
    },
    changed: false,
    commited: true,
    loading: true,
    js: '// type your code...',
    jsWidth: 50, jsHeight: 100,
  }

  prependCss = () => {
    const {uiKitStyles} = this.props
    const css = document.createElement('style')
    css.type = 'text/css'
    if (css.styleSheet) {
      css.styleSheet.cssText = uiKitStyles
    } else {
      css.appendChild(document.createTextNode(uiKitStyles))
    }
    document.head.prepend(css)
  }

  debouncedCompile =
    _.debounce(2000, ({css, js}) => doAction('compile', {css, js}).then(({view}) => {
      this.setState({view, compiling: false})
    }))

  onCssChange = (css, markers) => {
    if(markers.length) {
      return this.setState({
        css,
        changed: true,
        commited: false,
        error: {...this.state.error, css: true}
      })
    }
    this.setState({
      css,
      compiling: true,
      changed: true,
      commited: false,
      error: {...this.state.error, css: false}
    })
    this.debouncedCompile({css, js: this.state.js})
  }

  mergeState = (data) => this.setState({...this.state, ...data})

  onContentChange = (value) => {
    try {
      const content = JSON.parse(value)
      this.setState({
        content,
        contentStr: value,
        changed: true,
        commited: false,
        error: {...this.state.error, content: false}
      })
    } catch (e) {
      this.setState({
        contentStr: value,
        changed: true,
        commited: false,
        error: {...this.state.error, content: true}
      })
      console.log(e)
    }
  }

  onJsChange = (value, markers = []) => {
    if (markers.filter(({severity}) => severity > 2).length) {
      this.debouncedCompile.cancel()
      this.setState({
        js: value,
        changed: true,
        error: {...this.state.error, js: true}
      })
      return
    }
    this.setState({
      js: value,
      changed: true,
      compiling: true,
      error: {...this.state.error, js: false}
    })
    this.debouncedCompile({css: this.state.css, js: value})
  }

  renderPreview = () => {
    const {compiling, view, content, initial} = this.state
    return <Preview view={view} content={content} compiling={compiling} initial={initial}/>
  }

  updateDimentions = _.throttle(500, () => {
    setTimeout(() => {

      this.jsEditor && this.jsEditor.updateDimentions()
      this.cssEditor && this.cssEditor.updateDimentions()
      this.jsonEditor && this.jsonEditor.updateDimentions()
      if(!this.jsEditor || !this.cssEditor || !this.jsonEditor) return this.updateDimentions()
    }, 0)
  })

  resize = direction => event => {
    event.preventDefault();
    event.stopPropagation();
    const rect = findDOMNode(this.layoutComponent).getBoundingClientRect()
    const length = rect[direction == "we" ? "width" : "height"];
    const offset = rect[direction == "we" ? "top" : "left"];

    const onMouseMove = eventMove => {
      eventMove.preventDefault();
      eventMove.stopPropagation();
      const pos = eventMove[direction == "we" ? "clientX" : "clientY"] - offset;
      this.setState({
        [direction == "we" ? "jsWidth" : "jsHeight"]: Math.min(85, Math.max(15, pos/length*100))
      }, this.updateDimentions)
    }

    function onMouseUp(event) {
      document.removeEventListener("mousemove", onMouseMove, true);
      document.removeEventListener("mouseup", onMouseUp, true);
    }

    document.addEventListener("mousemove", onMouseMove, true);
    document.addEventListener("mouseup", onMouseUp, true);
  }

  horizontalResize = this.resize("ns")
  verticalResize = this.resize("we")

  toggleInitial = (initial) => this.setState({initial})

  render() {
    const {js, css, loading, contentStr, compiling, _id, initial} = this.state
    return loading
      ? <div>Loading</div>
      : (
        <div>
          <Controls {...this.state} mergeState={this.mergeState} />
          <div ref={ref => this.layoutComponent = ref}
            style={{display: 'flex', alignItems: "stretch", height: "calc(50vh - 12px)"}}>
            <Editor ref={ref => this.jsEditor = ref}
              changeSrc={this.onJsChange} src={js} language="javascript"
              width={`calc(${this.state.jsWidth}% - 2px)`}/>
            <div style={{width: '4px', cursor: "ew-resize"}} onMouseDown={this.verticalResize}/>
            <div style={{
              display: 'flex', flexDirection: 'column', width: `calc(${100-this.state.jsWidth}% - 2px)`,
            }}>
              <Editor ref={ref => this.cssEditor = ref}
                src={css} language="scss" changeSrc={this.onCssChange}
                height={`calc(${this.state.jsHeight}% - 2px)`}/>
              {contentStr && <div style={{height: '4px', cursor: "ns-resize"}} onMouseDown={this.horizontalResize}/>}
              {contentStr && <Editor ref={ref => this.jsonEditor = ref}
                src={contentStr} language="json" changeSrc={this.onContentChange}
                height={`calc(${100-this.state.jsHeight}% - 2px)`}
              />}
            </div>
          </div>
          <div style={{height: '4px'}}/>
          <div style={{position: 'relative'}}>
            <Tabs>
              <TabList>
                <Tab>Preview</Tab>
                <Tab>Images</Tab>
                <UiKitToggle onChange={this.toggleInitial} checked={initial}/>
              </TabList>
              <TabPanel>
                {compiling && <div className="loading-image"/>}
                <div style={{height: "calc(50vh - 34px)", overflowY: "auto"}}>
                  {this.renderPreview()}
                </div>
              </TabPanel>
              <TabPanel>
                <div style={{height: "calc(50vh - 34px)", overflowY: "auto"}}>
                  <Resources blockId={_id}/>
                </div>
              </TabPanel>
            </Tabs>

          </div>
        </div>
      )
  }
}
const UiKitToggle = ({onChange, checked}) => (
  <div>
    <input
      onChange={() => onChange(!checked)}
      type="checkbox"
      id="ui-kit-toggle"
      checked={checked}
      className="checkbox__input"
    />
    <label htmlFor="ui-kit-toggle">
      disable UI-kit
    </label>
  </div>
)

load().then(({store}) => render(
  <Provider store={store}>
    <BrowserRouter location="/" context={{}}>
      <Route path="*" render={() => <App/>}/>
    </BrowserRouter>
  </Provider>,
  document.getElementById('root')))
