import marked from 'marked'
import { autorun, extendObservable, observable } from 'mobx'

import { LS_ACCESS_TOKEN_KEY, LS_USER_KEY, NOT_INITIALIZED_ERROR } from './constants'
import { getTargetContainer, http, Query } from './utils'
import defaultTheme from './theme/default'

const scope = 'repo'

marked.setOptions({
  sanitize: true,
})

function extendRenderer(instance, renderer) {
  instance[renderer] = (container) => {
    const targetContainer = getTargetContainer(container)
    const render = instance.theme[renderer] || instance.defaultTheme[renderer]

    autorun(() => {
      const e = render(instance.state, instance)
      if (targetContainer.firstChild) {
        targetContainer.replaceChild(e, targetContainer.firstChild)
      } else {
        targetContainer.appendChild(e)
      }
    })

    return targetContainer
  }
}

class Comments {
  get accessToken() {
    return localStorage.getItem(LS_ACCESS_TOKEN_KEY)
  }
  set accessToken(token) {
    localStorage.setItem(LS_ACCESS_TOKEN_KEY, token)
  }

  get loginLink() {
    const oauthUri = 'https://github.com/login/oauth/authorize'
    const redirect_uri = this.oauth.redirect_uri || window.location.href

    const oauthParams = Object.assign({
      scope,
      redirect_uri,
    }, this.oauth)

    return `${oauthUri}${Query.stringify(oauthParams)}`
  }

  constructor(options = {}) {
    Object.assign(this, {
      marked,
      defaultTheme,
      id: window.location.href,
      title: window.document.title,
      link: window.location.href,
      desc: '',
      labels: [],
      theme: defaultTheme,
      oauth: {},
    }, options)

    const user = {}
    try {
      const userInfo = localStorage.getItem(LS_USER_KEY)
      if (this.accessToken && userInfo) {
        Object.assign(user, JSON.parse(userInfo), {
          fromCache: true,
        })
      }
    } catch (e) {
      localStorage.removeItem(LS_USER_KEY)
    }

    this.state = observable({
      user,
      error: null,
      meta: {},
      comments: undefined,
      reactions: [],
    })

    const renderers = Object.keys(this.theme)
    renderers.forEach(renderer => extendRenderer(this, renderer))

    const query = Query.parse()
    if (query.code) {
      const { client_id, client_secret } = this.oauth
      const code = query.code
      delete query.code
      const search = Query.stringify(query)
      history.replaceState({}, '', `${window.location.origin}${window.location.pathname}${search}${window.location.hash}`)

      this.state.user.loginning = true
      http.post('https://gh-oauth.imsun.net', {
          code,
          client_id,
          client_secret,
        }, '')
        .then(data => {
          this.accessToken = data.access_token
          this.update()
        })
        .catch(e => {
          this.user.loginning = false
          alert(e)
        })
    } else {
      this.update()
    }
  }

  init() {
    return this.createIssue()
      .then(() => this.load())
  }

  update() {
    return this.loadMeta()
      .then(() => Promise.all([
        this.loadUserInfo(),
        this.load(),
        this.loadReactions()
      ]))
      .catch(e => this.state.error = e)
  }

  createIssue() {
    const { id, owner, repo, title, link, desc, labels } = this

    return http.post(`/repos/${owner}/${repo}/issues`, {
      title,
      labels: labels.concat([id]),
      body: `${link}\n\n${desc}`,
    })
      .then((meta) => {
        this.state.meta = meta
        return meta
      })
  }

  getIssue() {
    if (this.state.meta.id) return Promise.resolve(this.state.meta)

    return this.loadMeta()
  }

  post(body) {
    return this.getIssue()
      .then(issue => http.post(issue.comments_url, { body }, ''))
  }

  loadMeta() {
    const { owner, repo } = this
    return http.get(`/repos/${owner}/${repo}/issues`, {
        labels: this.id,
      })
      .then(issues => {
        if (!issues.length) return Promise.reject(NOT_INITIALIZED_ERROR)
        this.state.meta = issues[0]
        return issues[0]
      })
  }

  load() {
    return this.getIssue()
      .then(issue => http.get(issue.comments_url, {}, ''))
      .then((comments) => {
        this.state.comments = comments
        return comments
      })
  }

  loadUserInfo() {
    if (!this.accessToken) {
      this.logout()
      return Promise.resolve({})
    }

    return http.get('/user')
      .then((user) => {
        this.state.user = user
        localStorage.setItem(LS_USER_KEY, JSON.stringify(user))
        return user
      })
  }

  loadReactions() {
    if (!this.accessToken) return Promise.resolve([])

    return this.getIssue()
      .then(issue => http.get(issue.reactions.url, {}, ''))
      .then((reactions) => {
        this.state.reactions = reactions
        return reactions
      })
  }

  login() {
    window.location.href = this.loginLink
  }

  logout() {
    localStorage.removeItem(LS_ACCESS_TOKEN_KEY)
    localStorage.removeItem(LS_USER_KEY)
    this.state.user = {}
  }

  like() {
    if (!this.accessToken) {
      alert('Login to Like')
      return Promise.reject()
    }

    const { owner, repo } = this

    return http.post(`/repos/${owner}/${repo}/issues/${this.state.meta.number}/reactions`, {
      content: 'heart',
    })
      .then(reaction => {
        this.state.reactions.push(reaction)
        this.state.meta.reactions.heart++
      })
  }

  unlike() {
    if (!this.accessToken) return Promise.reject()


    const { user, reactions } = this.state
    const index = reactions.findIndex(reaction => reaction.user.login === user.login)
    return http.delete(`/reactions/${reactions[index].id}`)
      .then(() => {
        reactions.splice(index, 1)
        this.state.meta.reactions.heart--
      })
  }
}

module.exports = Comments

@@ -313,16 +313,45 @@
 tions(%7B%0A
+  breaks: true,%0A  gfm: true,%0A
   saniti
@@ -3002,90 +3002,155 @@
 )%0A  
-%7D%0A%0A  update() %7B%0A    return this.loadMeta()%0A      .then(() =%3E Promise.all(%5B%0A       
+    .then(comments =%3E %7B%0A        this.state.error = null%0A        return comments%0A      %7D)%0A  %7D%0A%0A  update() %7B%0A    return Promise.all(%5Bthis.loadMeta(),
  thi
@@ -3165,26 +3165,50 @@
 erInfo()
-,
+%5D)
 %0A      
-  
+.then(() =%3E Promise.all(%5B
 this.loa
@@ -3211,24 +3211,16 @@
 .load(),
-%0A       
  this.lo
@@ -3232,23 +3232,16 @@
 ctions()
-%0A      
 %5D))%0A    
@@ -3846,32 +3846,36 @@
 () %7B%0A    const %7B
+ id,
  owner, repo %7D =
@@ -3932,24 +3932,48 @@
 /issues%60, %7B%0A
+        creator: owner,%0A
         labe
@@ -3976,21 +3976,16 @@
 labels: 
-this.
 id,%0A    

fix data loading