import db from '../../services/db'
import helper from '../../services/helpers'
import dayjs from 'dayjs'
import advanced from 'dayjs/plugin/advancedFormat'
import timezone from 'dayjs/plugin/timezone'
import relativeTime from 'dayjs/plugin/relativeTime'
import Fuse from 'fuse.js'
import cacheService from '../../services/cacheArticle'
import fever from '../../services/fever'
import feedbin from '../../services/feedbin'
import inoreader from '../../services/inoreader'
import greader from '../../services/greader'

dayjs.tz.setDefault(Intl.DateTimeFormat().resolvedOptions().timeZone)
dayjs.extend(relativeTime)
dayjs.extend(timezone)
dayjs.extend(advanced)

const sortBy = (key, pref) => {
  if (pref === 'asc') {
    return (a, b) => (a.articles[key] > b.articles[key] ? 1 : b.articles[key] > a.articles[key] ? -1 : 0)
  }
  return (a, b) => (a.articles[key] < b.articles[key] ? 1 : b.articles[key] < a.articles[key] ? -1 : 0)
}

const state = {
  articles: [],
  type: 'unread',
  search: '',
  fontSettingOn: false,
  fontSize: 100,
  fontStyle: null,
  category: null,
  feed: ''
}

const filters = {
  search: (articles, search) => articles.filter(article => article.articles.title.match(search)),
  unread: articles => articles.filter(article => !article.articles.read),
  played: articles => articles.filter(article => article.articles.played),
  read: articles => articles.filter(article => article.articles.read),
  favourites: articles => articles.filter(article => article.articles.favourite),
  feed: (articles, feed) => articles.filter(article => article.articles.feed_uuid === feed && !article.articles.read),
  category: (articles, category) => articles.filter(article => article.articles.category === category),
  saved: articles => articles.filter(article => article.articles.offline),
  all: articles => articles
}

const searchOption = {
  minMatchCharLength: 2,
  isCaseSensitive: false,
  shouldSort: true,
  findAllMatches: true,
  includeScore: true,
  threshold: 0.5,
  keys: ['articles.title', 'articles.content, articles.contentSnippet']
}

const getters = {
  filteredArticles: (state, getters, rootState) => {
    const sortPref = rootState.Setting.oldestArticles === 'off' ? 'desc' : 'asc'
    const orderedArticles = state.articles.concat().sort(sortBy('publishUnix', sortPref))
    if (state.type !== 'feed' && state.type !== 'search') {
      return filters[state.type](orderedArticles)
    }
    if (state.type === 'search') {
      const searchIndex = Fuse.createIndex(searchOption.keys, state.articles)
      const fuse = new Fuse(state.articles, searchOption, searchIndex)
      if (state.search !== '') {
        return fuse.search(state.search).map(article => article.item)
      }
      return filters.all(orderedArticles)
    }
    if (state.category) {
      return filters.category(orderedArticles, state.category)
    }
    return filters[state.type](orderedArticles, state.feed)
  }
}

const mutations = {
  LOAD_ARTICLES (state, data) {
    state.articles = Object.freeze(data.data)
  },
  MARK_ACTION (state, data) {
    const index = state.articles.findIndex(item => item.articles.uuid === data.data.id)
    const source = state.articles[index].articles.source
    if (data.rootState.Setting.fever_connected && source === 'fever') {
      fever.markItem(data.rootState.Setting.fever, data.data.type, [state.articles[index].articles.source_id])
    }
    if (data.rootState.Setting.feedbin_connected && source === 'feedbin') {
      feedbin.markItem(data.rootState.Setting.feedbin, data.data.type, [state.articles[index].articles.source_id])
    }
    if (data.rootState.Setting.selfhost_connected && source === 'greader') {
      greader.markItem(data.rootState.Setting.selfhost, data.data.type, [state.articles[index].articles.source_id])
    }
    if (data.rootState.Setting.inoreader_connected && source === 'inoreader') {
      inoreader.markItem(data.rootState.Setting.inoreader, data.data.type, [state.articles[index].articles.source_id])
    }
  },
  MARK_ALL_UNREAD (state, rootState) {
    const readArticles = state.articles.reduce((arr, item) => {
      if (item.articles.read) {
        arr.push(item.articles.uuid)
      }
      return arr
    }, [])
    const sourceIds = state.articles.reduce((arr, item) => {
      if (item.articles.read) {
        arr.push(item.articles.source_id)
      }
      return arr
    }, [])
    db.markAllunread(readArticles)
    if (rootState.Setting.fever_connected) {
      sourceIds.filter(item => item !== null).forEach((item) => {
        fever.markItem(rootState.Setting.fever, 'UNREAD', item)
      })
    }
    if (rootState.Setting.feedbin_connected) {
      feedbin.markItem(rootState.Setting.feedbin, 'UNREAD', sourceIds.filter(item => item !== null))
    }

    if (rootState.Setting.selfhost_connected) {
      greader.markItem(rootState.Setting.selfhost, 'UNREAD', sourceIds.filter(item => item !== null))
    }

    if (rootState.Setting.inoreader_connected) {
      inoreader.markItem(rootState.Setting.inoreader, 'UNREAD', sourceIds.filter(item => item !== null))
    }
  },
  MARK_ALL_READ (state, rootState) {
    const unreadArticles = state.articles.reduce((arr, item) => {
      if (!item.articles.read) {
        arr.push(item.articles.uuid)
      }
      return arr
    }, [])
    const sourceIds = state.articles.reduce((arr, item) => {
      if (!item.articles.read) {
        arr.push(item.articles.source_id)
      }
      return arr
    }, [])
    db.markAllRead(unreadArticles)
    if (rootState.Setting.fever_connected) {
      sourceIds.filter(item => item !== null).forEach((item) => {
        fever.markItem(rootState.Setting.fever, 'READ', item)
      })
    }
    if (rootState.Setting.feedbin_connected) {
      feedbin.markItem(rootState.Setting.feedbin, 'READ', sourceIds.filter(item => item !== null))
    }

    if (rootState.Setting.selfhost_connected) {
      greader.markItem(rootState.Setting.selfhost, 'READ', sourceIds.filter(item => item !== null))
    }

    if (rootState.Setting.inoreader_connected) {
      inoreader.markItem(rootState.Setting.inoreader, 'READ', sourceIds.filter(item => item !== null))
    }
  },
  DELETE_ARTICLES (state, id) {
    const articles = state.articles.filter(item => item.feed_id === id)
    for (let i = 0; i < articles.length; i++) {
      cacheService.uncache(`raven-${articles[i]._id}`)
    }
    db.deleteArticles(id)
  },
  REFRESH_FEEDS (state, feeds) {
    if (feeds.length > 0) {
      helper.subscribe(feeds.filter(item => item.source === 'local' || typeof item.source === 'undefined'), null, true)
    }
  },
  CHANGE_TYPE (state, type) {
    state.type = type.type
    state.category = type.category
    state.feed = type.feed
    state.search = type.search
  },
  SAVE_ARTICLE (state, data) {
    const index = state.articles.findIndex(item => item.articles.uuid === data.article._id)
    state.articles[index].offline = data.type === 'CACHE'
  },
  INCREASE_FONT (state) {
    if (state.fontSize <= 150) {
      state.fontSize += 5
    }
  },
  DRECREASE_FONT (state) {
    if (state.fontSize !== 100) {
      state.fontSize -= 5
    }
  },
  FONT_SETTINGS_ON (state, data) {
    state.fontSettingOn = data
  },
  CHANGE_FONT_STYLE (state, data) {
    state.fontStyle = data
  },
  UPDATE_ARTICLE_CATEGORY (state, data) {
    const articles = state.articles.filter(item => item.articles.category === data.old.title)
      .map(item => item.articles.uuid)
    db.updateArticleCategory(articles, data.new.title)
  },
  MARK_CATEGORY_READ (state, data) {
    db.markCategoryRead(data)
  }
}

const actions = {
  async loadArticles ({ commit, rootState }) {
    commit('LOAD_ARTICLES', {
      root: rootState,
      data: await db.fetchArticles()
    })
  },
  markAction ({ commit, rootState }, data) {
    switch (data.type) {
      case 'FAVOURITE':
        db.markFavourite(data.id, true)
        break
      case 'UNFAVOURITE':
        db.markFavourite(data.id, false)
        break
      case 'READ':
        db.markRead(data.id, data.podcast, true)
        break
      case 'UNREAD':
        db.markRead(data.id, data.podcast, false)
        break
    }
    commit('MARK_ACTION', { rootState: rootState, data: data })
  },
  saveArticle ({ commit }, data) {
    db.markOffline(data.article._id, data.type === 'CACHE')
    commit('SAVE_ARTICLE', data)
  },
  markAllRead ({ commit, rootState }) {
    commit('MARK_ALL_READ', rootState)
  },
  markAllUnread ({ commit, rootState }) {
    commit('MARK_ALL_UNREAD', rootState)
  },
  async deleteArticle ({ dispatch, commit }, id) {
    commit('DELETE_ARTICLES', id)
    await dispatch('loadArticles')
  },
  async refreshFeeds ({ dispatch, commit }) {
    commit('REFRESH_FEEDS', await db.fetchFeeds())
  },
  changeType ({ commit }, type) {
    commit('CHANGE_TYPE', type)
  },
  decreaseFont ({ commit }) {
    commit('DRECREASE_FONT')
  },
  increaseFont ({ commit }) {
    commit('INCREASE_FONT')
  },
  changeFontStyle ({ commit }, font) {
    commit('CHANGE_FONT_STYLE', font)
  },
  turnOnFontSetting ({ commit }, data) {
    commit('FONT_SETTINGS_ON', data)
  },
  updateArticleCategory ({ commit }, data) {
    commit('UPDATE_ARTICLE_CATEGORY', data)
  },
  updateArticleFeedTitle ({ commit }, data) {
    db.updateArticleCategoryFeed(data.id, data.category)
  },
  markCategoryRead ({
    commit
  }, id) {
    commit('MARK_CATEGORY_READ', id)
  }
}

export default {
  state,
  getters,
  mutations,
  actions
}
