import api, { getLinks } from '../api'
import openDB from '../storage/db'
import {
  importAccount,
  importFetchedAccount,
  importFetchedAccounts,
  importErrorWhileFetchingAccountByUsername,
} from './importer'
import { me } from '../initial_state'

export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS'
export const ACCOUNT_FETCH_FAIL    = 'ACCOUNT_FETCH_FAIL'

export const ACCOUNT_FOLLOW_REQUEST = 'ACCOUNT_FOLLOW_REQUEST'
export const ACCOUNT_FOLLOW_SUCCESS = 'ACCOUNT_FOLLOW_SUCCESS'
export const ACCOUNT_FOLLOW_FAIL    = 'ACCOUNT_FOLLOW_FAIL'

export const ACCOUNT_UNFOLLOW_REQUEST = 'ACCOUNT_UNFOLLOW_REQUEST'
export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS'
export const ACCOUNT_UNFOLLOW_FAIL    = 'ACCOUNT_UNFOLLOW_FAIL'

export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST'
export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS'
export const ACCOUNT_BLOCK_FAIL    = 'ACCOUNT_BLOCK_FAIL'

export const ACCOUNT_UNBLOCK_REQUEST = 'ACCOUNT_UNBLOCK_REQUEST'
export const ACCOUNT_UNBLOCK_SUCCESS = 'ACCOUNT_UNBLOCK_SUCCESS'
export const ACCOUNT_UNBLOCK_FAIL    = 'ACCOUNT_UNBLOCK_FAIL'

export const ACCOUNT_MUTE_REQUEST = 'ACCOUNT_MUTE_REQUEST'
export const ACCOUNT_MUTE_SUCCESS = 'ACCOUNT_MUTE_SUCCESS'
export const ACCOUNT_MUTE_FAIL    = 'ACCOUNT_MUTE_FAIL'

export const ACCOUNT_UNMUTE_REQUEST = 'ACCOUNT_UNMUTE_REQUEST'
export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS'
export const ACCOUNT_UNMUTE_FAIL    = 'ACCOUNT_UNMUTE_FAIL'

export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST'
export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS'
export const FOLLOWERS_FETCH_FAIL    = 'FOLLOWERS_FETCH_FAIL'

export const FOLLOWERS_EXPAND_REQUEST = 'FOLLOWERS_EXPAND_REQUEST'
export const FOLLOWERS_EXPAND_SUCCESS = 'FOLLOWERS_EXPAND_SUCCESS'
export const FOLLOWERS_EXPAND_FAIL    = 'FOLLOWERS_EXPAND_FAIL'

export const FOLLOWING_FETCH_REQUEST = 'FOLLOWING_FETCH_REQUEST'
export const FOLLOWING_FETCH_SUCCESS = 'FOLLOWING_FETCH_SUCCESS'
export const FOLLOWING_FETCH_FAIL    = 'FOLLOWING_FETCH_FAIL'

export const FOLLOWING_EXPAND_REQUEST = 'FOLLOWING_EXPAND_REQUEST'
export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS'
export const FOLLOWING_EXPAND_FAIL    = 'FOLLOWING_EXPAND_FAIL'

export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST'
export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS'
export const RELATIONSHIPS_FETCH_FAIL    = 'RELATIONSHIPS_FETCH_FAIL'

export const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST'
export const FOLLOW_REQUESTS_FETCH_SUCCESS = 'FOLLOW_REQUESTS_FETCH_SUCCESS'
export const FOLLOW_REQUESTS_FETCH_FAIL    = 'FOLLOW_REQUESTS_FETCH_FAIL'

export const FOLLOW_REQUESTS_EXPAND_REQUEST = 'FOLLOW_REQUESTS_EXPAND_REQUEST'
export const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS'
export const FOLLOW_REQUESTS_EXPAND_FAIL    = 'FOLLOW_REQUESTS_EXPAND_FAIL'

export const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST'
export const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS'
export const FOLLOW_REQUEST_AUTHORIZE_FAIL    = 'FOLLOW_REQUEST_AUTHORIZE_FAIL'

export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST'
export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS'
export const FOLLOW_REQUEST_REJECT_FAIL    = 'FOLLOW_REQUEST_REJECT_FAIL'

/**
 * 
 */
function getFromDB(dispatch, getState, index, id) {
  return new Promise((resolve, reject) => {
    const request = index.get(id)

    request.onerror = reject

    request.onsuccess = () => {
      if (!request.result) {
        reject()
        return
      }

      dispatch(importAccount(request.result))
      resolve(request.result.moved && getFromDB(dispatch, getState, index, request.result.moved))
    }
  })
}

/**
 * 
 */
export const fetchAccount = (id) => (dispatch, getState) => {
  if (id === -1 || getState().getIn(['accounts', id], null) !== null) return
  if (!id) return

  dispatch(fetchRelationships([id]))
  dispatch(fetchAccountRequest(id))

  openDB().then((db) => getFromDB(
    dispatch,
    getState,
    db.transaction('accounts', 'read').objectStore('accounts').index('id'),
    id
  ).then(() => db.close(), (error) => {
    db.close()
    throw error
  })).catch(() => api(getState).get(`/api/v1/accounts/${id}`).then((response) => {
    dispatch(importFetchedAccount(response.data))
  })).then(() => {
    dispatch(fetchAccountSuccess())
  }).catch((error) => {
    dispatch(fetchAccountFail(id, error))
  })
}

/**
 * 
 */
export const fetchAccountByUsername = (username) => (dispatch, getState) => {
  if (!username) return

  api(getState).get(`/api/v1/account_by_username/${username}`).then((response) => {
    dispatch(importFetchedAccount(response.data))
    dispatch(fetchRelationships([response.data.id]))
  }).then(() => {
    dispatch(fetchAccountSuccess())
  }).catch((error) => {
    dispatch(fetchAccountFail(null, error))
    dispatch(importErrorWhileFetchingAccountByUsername(username))
  })
}

const fetchAccountRequest = (id) => ({
  type: ACCOUNT_FETCH_REQUEST,
  id,
})

const fetchAccountSuccess = () => ({
  type: ACCOUNT_FETCH_SUCCESS,
})

const fetchAccountFail = (id, error) => ({
  type: ACCOUNT_FETCH_FAIL,
  id,
  error,
})

/**
 * 
 */
export const followAccount = (id, reblogs = true) => (dispatch, getState) => {
  if (!me) return

  const alreadyFollowing = getState().getIn(['relationships', id, 'following'])
  const locked = getState().getIn(['accounts', id, 'locked'], false)

  dispatch(followAccountRequest(id, locked))

  api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then((response) => {
    dispatch(followAccountSuccess(response.data, alreadyFollowing))
  }).catch((error) => {
    dispatch(followAccountFail(error, locked))
  })
}

const followAccountRequest = (id, locked) => ({
  type: ACCOUNT_FOLLOW_REQUEST,
  id,
  locked,
})

const followAccountSuccess = (relationship, alreadyFollowing) => ({
  type: ACCOUNT_FOLLOW_SUCCESS,
  showToast: true,
  relationship,
  alreadyFollowing,
})

const followAccountFail = (error, locked) => ({
  type: ACCOUNT_FOLLOW_FAIL,
  showToast: true,
  error,
  locked,
})

/**
 * 
 */
export const unfollowAccount = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(unfollowAccountRequest(id))

  api(getState).post(`/api/v1/accounts/${id}/unfollow`).then((response) => {
    dispatch(unfollowAccountSuccess(response.data, getState().get('statuses')))
  }).catch((error) => {
    dispatch(unfollowAccountFail(error))
  })
}

const unfollowAccountRequest = (id) => ({
  type: ACCOUNT_UNFOLLOW_REQUEST,
  id,
})

const unfollowAccountSuccess = (relationship, statuses) => ({
  type: ACCOUNT_UNFOLLOW_SUCCESS,
  showToast: true,
  relationship,
  statuses,
})

const unfollowAccountFail = (error) => ({
  type: ACCOUNT_UNFOLLOW_FAIL,
  showToast: true,
  error,
})

/**
 * 
 */
export const blockAccount = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(blockAccountRequest(id))

  api(getState).post(`/api/v1/accounts/${id}/block`).then((response) => {
    // : todo : remove gay stuff like passing entire status below:
    // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
    dispatch(blockAccountSuccess(response.data, getState().get('statuses')))
  }).catch((error) => {
    dispatch(blockAccountFail(id, error))
  })
}

const blockAccountRequest = (id) => ({
  type: ACCOUNT_BLOCK_REQUEST,
  id,
})

const blockAccountSuccess = (relationship, statuses) => ({
  type: ACCOUNT_BLOCK_SUCCESS,
  showToast: true,
  relationship,
  statuses,
})

const blockAccountFail = (error) => ({
  type: ACCOUNT_BLOCK_FAIL,
  showToast: true,
  error,
})

/**
 * 
 */
export const unblockAccount = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(unblockAccountRequest(id))

  api(getState).post(`/api/v1/accounts/${id}/unblock`).then((response) => {
    dispatch(unblockAccountSuccess(response.data))
  }).catch((error) => {
    dispatch(unblockAccountFail(id, error))
  })
}

const unblockAccountRequest = (id) => ({
  type: ACCOUNT_UNBLOCK_REQUEST,
  id,
})

const unblockAccountSuccess = (relationship) => ({
  type: ACCOUNT_UNBLOCK_SUCCESS,
  showToast: true,
  relationship,
})

const unblockAccountFail = (error) => ({
  type: ACCOUNT_UNBLOCK_FAIL,
  showToast: true,
  error,
})

/**
 * 
 */
export const muteAccount = (id, notifications) => (dispatch, getState) => {
  if (!me) return

  dispatch(muteAccountRequest(id))

  api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications }).then((response) => {
    // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
    dispatch(muteAccountSuccess(response.data, getState().get('statuses')))
  }).catch((error) => {
    dispatch(muteAccountFail(id, error))
  })
}

const muteAccountRequest = (id) => ({
  type: ACCOUNT_MUTE_REQUEST,
  id,
})

const muteAccountSuccess = (relationship, statuses) => ({
  type: ACCOUNT_MUTE_SUCCESS,
  showToast: true,
  relationship,
  statuses,
})

const muteAccountFail = (error) => ({
  type: ACCOUNT_MUTE_FAIL,
  showToast: true,
  error,
})

/**
 * 
 */
export const unmuteAccount = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(unmuteAccountRequest(id))

  api(getState).post(`/api/v1/accounts/${id}/unmute`).then((response) => {
    dispatch(unmuteAccountSuccess(response.data))
  }).catch((error) => {
    dispatch(unmuteAccountFail(id, error))
  })
}

const unmuteAccountRequest = (id) => ({
  type: ACCOUNT_UNMUTE_REQUEST,
  id,
})

const unmuteAccountSuccess = (relationship) => ({
  type: ACCOUNT_UNMUTE_SUCCESS,
  showToast: true,
  relationship,
})

const unmuteAccountFail = (error) => ({
  type: ACCOUNT_UNMUTE_FAIL,
  showToast: true,
  error,
})

/**
 * 
 */
export const fetchFollowers = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(fetchFollowersRequest(id))

  api(getState).get(`/api/v1/accounts/${id}/followers`).then((response) => {
    const next = getLinks(response).refs.find(link => link.rel === 'next')

    dispatch(importFetchedAccounts(response.data))
    dispatch(fetchFollowersSuccess(id, response.data, next ? next.uri : null))
    dispatch(fetchRelationships(response.data.map(item => item.id)))
  }).catch((error) => {
    dispatch(fetchFollowersFail(id, error))
  })
}

const fetchFollowersRequest = (id) => ({
  type: FOLLOWERS_FETCH_REQUEST,
  id,
})

const fetchFollowersSuccess = (id, accounts, next) => ({
  type: FOLLOWERS_FETCH_SUCCESS,
  id,
  accounts,
  next,
})

const fetchFollowersFail = (id, error) => ({
  type: FOLLOWERS_FETCH_FAIL,
  showToast: true,
  id,
  error,
})

/**
 * 
 */
export const expandFollowers = (id) => (dispatch, getState) => {
  if (!me) return

  const url = getState().getIn(['user_lists', 'followers', id, 'next'])
  const isLoading = getState().getIn(['user_lists', 'followers', id, 'isLoading'])

  if (url === null || isLoading) return

  dispatch(expandFollowersRequest(id))

  api(getState).get(url).then((response) => {
    const next = getLinks(response).refs.find(link => link.rel === 'next')

    dispatch(importFetchedAccounts(response.data))
    dispatch(expandFollowersSuccess(id, response.data, next ? next.uri : null))
    dispatch(fetchRelationships(response.data.map(item => item.id)))
  }).catch((error) => {
    dispatch(expandFollowersFail(id, error))
  })
}

const expandFollowersRequest = (id) => ({
  type: FOLLOWERS_EXPAND_REQUEST,
  id,
})

const expandFollowersSuccess = (id, accounts, next) => ({
  type: FOLLOWERS_EXPAND_SUCCESS,
  id,
  accounts,
  next,
})

const expandFollowersFail = (id, error) => ({
  type: FOLLOWERS_EXPAND_FAIL,
  showToast: true,
  id,
  error,
})

/**
 * 
 */
export const fetchFollowing = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(fetchFollowingRequest(id))

  api(getState).get(`/api/v1/accounts/${id}/following`).then((response) => {
    const next = getLinks(response).refs.find(link => link.rel === 'next')

    dispatch(importFetchedAccounts(response.data))
    dispatch(fetchFollowingSuccess(id, response.data, next ? next.uri : null))
    dispatch(fetchRelationships(response.data.map(item => item.id)))
  }).catch((error) => {
    dispatch(fetchFollowingFail(id, error))
  })
}

const fetchFollowingRequest = (id) => ({
  type: FOLLOWING_FETCH_REQUEST,
  id,
})

const fetchFollowingSuccess = (id, accounts, next) => ({
  type: FOLLOWING_FETCH_SUCCESS,
  id,
  accounts,
  next,
})

const fetchFollowingFail = (id, error) => ({
  type: FOLLOWING_FETCH_FAIL,
  showToast: true,
  id,
  error,
})

/**
 * 
 */
export const expandFollowing = (id) => (dispatch, getState) => {
  if (!me) return

  const url = getState().getIn(['user_lists', 'following', id, 'next'])
  const isLoading = getState().getIn(['user_lists', 'following', id, 'isLoading'])

  if (url === null || isLoading) return

  dispatch(expandFollowingRequest(id))

  api(getState).get(url).then((response) => {
    const next = getLinks(response).refs.find(link => link.rel === 'next')

    dispatch(importFetchedAccounts(response.data))
    dispatch(expandFollowingSuccess(id, response.data, next ? next.uri : null))
    dispatch(fetchRelationships(response.data.map(item => item.id)))
  }).catch((error) => {
    dispatch(expandFollowingFail(id, error))
  })
}

const expandFollowingRequest = (id) => ({
  type: FOLLOWING_EXPAND_REQUEST,
  id,
})

const expandFollowingSuccess = (id, accounts, next) => ({
  type: FOLLOWING_EXPAND_SUCCESS,
  id,
  accounts,
  next,
})

const expandFollowingFail = (id, error) => ({
  type: FOLLOWING_EXPAND_FAIL,
  showToast: true,
  id,
  error,
})

/**
 * 
 */
export const fetchRelationships = (accountIds) => (dispatch, getState) => {
  if (!me) return

  const loadedRelationships = getState().get('relationships')
  let newAccountIds = accountIds.filter((id) => {
    if (id === me) return false
    return loadedRelationships.get(id, null) === null
  })

  if (newAccountIds.length === 0) return
  
  if (newAccountIds.length == 1) {
    const firstId = newAccountIds[0]
    if (me === firstId) return
  }

  // Unique
  newAccountIds = Array.from(new Set(newAccountIds))

  dispatch(fetchRelationshipsRequest(newAccountIds))

  api(getState).post('/api/v1/accounts/relationships', {
    accountIds: newAccountIds,
  }).then((response) => {
    dispatch(fetchRelationshipsSuccess(response.data))
  }).catch((error) => {
    dispatch(fetchRelationshipsFail(error))
  })
}

const fetchRelationshipsRequest = (ids) => ({
  type: RELATIONSHIPS_FETCH_REQUEST,
  ids,
})

const fetchRelationshipsSuccess = (relationships) => ({
  type: RELATIONSHIPS_FETCH_SUCCESS,
  relationships,
})

const fetchRelationshipsFail = (error) => ({
  type: RELATIONSHIPS_FETCH_FAIL,
  error,
})

/**
 * 
 */
export const fetchFollowRequests = () => (dispatch, getState) => {
  if (!me) return

  dispatch(fetchFollowRequestsRequest())

  api(getState).get('/api/v1/follow_requests').then((response) => {
    const next = getLinks(response).refs.find(link => link.rel === 'next')
    dispatch(importFetchedAccounts(response.data))
    dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null))
  }).catch((error) => dispatch(fetchFollowRequestsFail(error)))
}

const fetchFollowRequestsRequest = () => ({
  type: FOLLOW_REQUESTS_FETCH_REQUEST,
})

const fetchFollowRequestsSuccess = (accounts, next) => ({
  type: FOLLOW_REQUESTS_FETCH_SUCCESS,
  accounts,
  next,
})

const fetchFollowRequestsFail = (error) => ({
  type: FOLLOW_REQUESTS_FETCH_FAIL,
  showToast: true,
  error,
})

/**
 * 
 */
export const expandFollowRequests = () => (dispatch, getState) => {
  if (!me) return

  const url = getState().getIn(['user_lists', 'follow_requests', me, 'next'])
  const isLoading = getState().getIn(['user_lists', 'follow_requests', me, 'isLoading'])

  if (url === null || isLoading) return

  dispatch(expandFollowRequestsRequest())

  api(getState).get(url).then((response) => {
    const next = getLinks(response).refs.find(link => link.rel === 'next')
    dispatch(importFetchedAccounts(response.data))
    dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null))
  }).catch((error) => dispatch(expandFollowRequestsFail(error)))
}

const expandFollowRequestsRequest = () => ({
  type: FOLLOW_REQUESTS_EXPAND_REQUEST,
})

const expandFollowRequestsSuccess = (accounts, next) => ({
  type: FOLLOW_REQUESTS_EXPAND_SUCCESS,
  accounts,
  next,
})

const expandFollowRequestsFail = (error) => ({
  type: FOLLOW_REQUESTS_EXPAND_FAIL,
  showToast: true,
  error,
})

/**
 * 
 */
export const authorizeFollowRequest = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(authorizeFollowRequestRequest(id))

  api(getState)
    .post(`/api/v1/follow_requests/${id}/authorize`)
    .then(() => dispatch(authorizeFollowRequestSuccess(id)))
    .catch((error) => dispatch(authorizeFollowRequestFail(id, error)))
}

const authorizeFollowRequestRequest = (id) => ({
  type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
  id,
})

const authorizeFollowRequestSuccess = (id) => ({
  type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
  showToast: true,
  id,
})

const authorizeFollowRequestFail = (id, error) => ({
  type: FOLLOW_REQUEST_AUTHORIZE_FAIL,
  showToast: true,
  id,
  error,
})

/**
 * 
 */
export const rejectFollowRequest = (id) => (dispatch, getState) => {
  if (!me) return

  dispatch(rejectFollowRequestRequest(id))

  api(getState)
    .post(`/api/v1/follow_requests/${id}/reject`)
    .then(() => dispatch(rejectFollowRequestSuccess(id)))
    .catch((error) => dispatch(rejectFollowRequestFail(id, error)))
}

const rejectFollowRequestRequest = (id) => ({
  type: FOLLOW_REQUEST_REJECT_REQUEST,
  id,
})

const rejectFollowRequestSuccess = (id) => ({
  type: FOLLOW_REQUEST_REJECT_SUCCESS,
  showToast: true,
  id,
})

const rejectFollowRequestFail = (id, error) => ({
  type: FOLLOW_REQUEST_REJECT_FAIL,
  showToast: true,
  id,
  error,
})

