import React, { useState, useEffect } from 'react'
import { useQuery } from 'react-query'
import Head from 'next/head'

import LoadingIndicator from '../components/LoadingIndicator'
import Sleep from '../components/sleep'
import NewsEntry from '../components/newsEntry'
import Composer from '../components/composer'
import Modal from '../components/modal'

import styles from './memes.module.css'
import constants from '../constants'

const forumPostPerPage = 5
const frontpageForumName = 'memes'

const LeaderBoard = ({ leaderBoard, userId }) => {
  if (!leaderBoard) return <LoadingIndicator />
  return (
    <div className={styles.msg}>
      <div style={{ padding: 8 }}>Ranglista</div>
      <div className={styles.leaderBoard}>
        <div></div>
        <div>User #</div>
        <div>Up</div>
        <div>Down</div>
        <div>Sum</div>
      </div>
      {leaderBoard &&
        leaderBoard.map((x, i) => {
          if (i > 9) return null
          const { up, down, user, sum } = x
          return (
            <div
              className={`${styles.leaderBoard} ${
                userId === parseInt(user) && styles.selfRow
              }`}
              key={user}
            >
              <div>{`${i + 1}.`}</div>
              <div>#{user}</div>
              <div>{up}</div>
              <div>{down}</div>
              <div>{sum}</div>
            </div>
          )
        })}
    </div>
  )
}

const Header = ({ leaderBoard, userId }) => {
  return (
    <div className={styles.header}>
      <div className={styles.msg}>
        Mivel mostanában elég népszerű lett az oldal, ezért egy új rész lett
        hozzáadva: Memes
        <p /> Ide lehet posztolni akármilyen vicces tartalmat, és upvoteolni /
        downvoteolni lehet a többiek által feltöltötteket
        <p />
        Have fun! :)
      </div>
      <LeaderBoard leaderBoard={leaderBoard} userId={userId} />
    </div>
  )
}

function fetchLeaderboard() {
  const { data } = useQuery(
    'leaderBoard',
    () => {
      return new Promise((resolve) => {
        fetch(`${constants.apiUrl}forumRanklist?forumName=memes`, {
          credentials: 'include',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
        })
          .then((res) => {
            return res.json()
          })
          .then((res) => {
            resolve(res)
          })
      })
    },
    { staleTime: Infinity }
  )
  return data || []
}

function fetchEntry(postKey) {
  return new Promise((resolve) => {
    fetch(
      `${constants.apiUrl}forumEntry?forumName=${frontpageForumName}&postKey=${postKey}`,
      {
        credentials: 'include',
      }
    )
      .then((resp) => {
        return resp.json()
      })
      .then((res) => {
        resolve(res)
      })
  })
}

function fetchForum(from) {
  return new Promise((resolve) => {
    fetch(
      `${
        constants.apiUrl
      }forumEntries?forumName=${frontpageForumName}&getContent=true${
        from ? `&from=${from}` : ''
      }&count=${forumPostPerPage}`,
      {
        credentials: 'include',
      }
    )
      .then((resp) => {
        return resp.json()
      })
      .then((res) => {
        resolve(res)
      })
  })
}

function addPost(title, content, file) {
  return new Promise((resolve) => {
    fetch(constants.apiUrl + 'addPost', {
      method: 'POST',
      credentials: 'include',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        forumName: frontpageForumName,
        title: title,
        content: content,
        image: file.name,
      }),
    })
      .then((res) => {
        return res.json()
      })
      .then((res) => {
        const formData = new FormData() // eslint-disable-line
        formData.append('file', file)

        fetch(constants.apiUrl + 'postMeme', {
          method: 'POST',
          credentials: 'include',
          headers: {
            Accept: 'application/json',
          },
          body: formData,
        }).then((imgPostRes) => {
          console.info(imgPostRes)
          resolve(res)
        })
      })
  })
}

function updateForumPost(forum, postKey, postData) {
  return Object.keys(forum).reduce((acc, key) => {
    const entry = forum[key]
    if (key === postKey) {
      acc = {
        ...acc,
        [key]: postData,
      }
    } else {
      acc = {
        ...acc,
        [key]: entry,
      }
    }
    return acc
  }, {})
}

const NewsEntryContainer = ({
  postKey,
  setNews,
  news,
  userId,
  newsEntryData,
  onTitleClick,
}) => {
  const [error, setError] = useState(false)

  useEffect(() => {
    if (!newsEntryData && !error) {
      fetchEntry(postKey)
        .then((res) => {
          const { success, entry, msg } = res
          if (success) {
            setNews({ [postKey]: entry, ...news })
          } else {
            alert(msg)
            setError(true)
          }
        })
        .catch((e) => {
          console.error(e)
          setError(true)
        })
    }
  }, [])

  if (!newsEntryData) {
    return <LoadingIndicator />
  }

  if (error) {
    return <div>Lil fuckup</div>
  }

  return (
    <NewsEntry
      showUpDownVote
      onTitleClick={onTitleClick}
      uid={userId}
      key={postKey}
      newsKey={postKey}
      newsItem={newsEntryData}
      onPostDelete={() => {
        fetch(constants.apiUrl + 'rmPost', {
          method: 'POST',
          credentials: 'include',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            forumName: frontpageForumName,
            postKey: postKey,
          }),
        })
          .then((res) => {
            return res.json()
          })
          .then((res) => {
            const { success, msg } = res
            if (success) {
              setNews(
                Object.keys(news).reduce((acc, key) => {
                  const entry = news[key]
                  if (key !== postKey) {
                    acc = {
                      ...acc,
                      [key]: entry,
                    }
                  }
                  return acc
                }, {})
              )
            } else {
              alert(msg)
            }
          })
      }}
      onNewsReact={({ reaction, isDelete }) => {
        fetch(constants.apiUrl + 'react', {
          method: 'POST',
          credentials: 'include',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            reaction: reaction,
            postKey: postKey,
            isDelete: isDelete,
            forumName: frontpageForumName,
          }),
        })
          .then((res) => {
            return res.json()
          })
          .then((res) => {
            setNews(updateForumPost(news, postKey, res.postData))
          })
      }}
      onCommentReact={({ path, reaction, isDelete }) => {
        fetch(constants.apiUrl + 'react', {
          method: 'POST',
          credentials: 'include',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            type: 'reaction',
            postKey: postKey,
            path: path,
            reaction: reaction,
            isDelete: isDelete,
            forumName: frontpageForumName,
          }),
        })
          .then((res) => {
            return res.json()
          })
          .then((res) => {
            const { success, postData, msg } = res
            if (success) {
              setNews(updateForumPost(news, postKey, postData))
            } else {
              alert(msg)
            }
          })
      }}
      onDelete={(path) => {
        fetch(constants.apiUrl + 'comment', {
          method: 'POST',
          credentials: 'include',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            type: 'delete',
            path: path,
            postKey: postKey,
            forumName: frontpageForumName,
          }),
        })
          .then((res) => {
            return res.json()
          })
          .then((res) => {
            const { success, postData, msg } = res
            if (success) {
              setNews(updateForumPost(news, postKey, postData))
            } else {
              alert(msg)
            }
          })
      }}
      onComment={(path, content) => {
        fetch(constants.apiUrl + 'comment', {
          method: 'POST',
          credentials: 'include',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            type: 'add',
            path: path,
            content: content,
            postKey: postKey,
            forumName: frontpageForumName,
          }),
        })
          .then((res) => {
            return res.json()
          })
          .then((res) => {
            const { success, postData, msg } = res
            if (success) {
              setNews(updateForumPost(news, postKey, postData))
            } else {
              alert(msg)
            }
          })
      }}
    />
  )
}

export default function Memes({
  router,
  globalData,
  globalState,
  setGlobalState,
}) {
  const userId = globalData.userId
  const [news, setNews] = useState(null)
  const [nextEntryKey, setNextEntryKey] = useState()
  const [fetchingForum, setFetchingForum] = useState(false)
  const [postInModalKey, setPostInModalKey] = useState()
  const [isUploading, setIsUploading] = useState(false)
  const { leaderBoard } = fetchLeaderboard()

  useEffect(() => {
    if (globalState.memes) {
      const { entries, nextKey } = globalState.memes
      setNextEntryKey(nextKey)
      setNews(entries)
    } else {
      setFetchingForum(true)
      fetchForum().then((res) => {
        setFetchingForum(false)
        const { entries, nextKey } = res
        setNextEntryKey(nextKey)
        setNews(entries)
        setGlobalState({ memes: res })
      })
    }
  }, [])

  useEffect(() => {
    const postKey = router.query.postKey
      ? decodeURIComponent(router.query.postKey)
      : ''

    if (postKey) {
      setPostInModalKey(postKey)
    }
  }, [router.query.postKey])

  const renderNews = () => {
    if (news) {
      const newsItems = Object.keys(news).map((postKey) => {
        const newsEntryData = news[postKey]
        return (
          <NewsEntryContainer
            onTitleClick={() => {
              setPostInModalKey(postKey)
              router.replace(
                `${router.pathname}?postKey=${encodeURIComponent(postKey)}`,
                undefined,
                { shallow: true }
              )
            }}
            key={postKey}
            postKey={postKey}
            setNews={setNews}
            news={news}
            userId={userId}
            newsEntryData={newsEntryData}
          />
        )
      })

      return (
        <div>
          <Composer
            allowFile
            fileOnly
            onSubmit={(title, content, file) => {
              if (!file) return
              setIsUploading(true)
              addPost(title, content, file).then((res) => {
                const { success, newPostKey, newEntry, msg } = res
                if (success) {
                  setNews({ [newPostKey]: newEntry, ...news })
                } else {
                  alert(msg)
                }
                setIsUploading(false)
              })
            }}
          />
          {isUploading && (
            <div
              style={{
                display: 'flex',
                alignItems: 'center',
                flexDirection: 'column',
              }}
            >
              {'Feltöltés ...'}
              <LoadingIndicator />
            </div>
          )}
          <div>{newsItems}</div>
          {nextEntryKey ? (
            <div
              className={styles.loadMoreButton}
              onClick={() => {
                if (fetchingForum) {
                  return
                }

                setFetchingForum(true)
                fetchForum(nextEntryKey).then((res) => {
                  setFetchingForum(false)

                  const { entries, nextKey } = res
                  setNextEntryKey(nextKey)
                  setNews({ ...news, ...entries })
                  setGlobalState({
                    news: {
                      entries: { ...news, ...entries },
                      nextKey: nextKey,
                    },
                  })
                })
              }}
            >
              {fetchingForum ? (
                <LoadingIndicator />
              ) : (
                'Több bejegyzés betöltése'
              )}
            </div>
          ) : (
            <div
              style={{
                padding: 16,
                display: 'flex',
                justifyContent: 'center',
                fontStyle: 'italic',
              }}
            >
              {'The end'}
            </div>
          )}
        </div>
      )
    } else {
      return <LoadingIndicator />
    }
  }

  return (
    <div>
      <Head>
        <title>Qmining | Frylabs.net</title>
      </Head>
      <Header leaderBoard={leaderBoard} userId={userId} />
      <div className={styles.description}>
        {
          'A "Memes" rész Április 8-ig lesz elérhető, vagy ha népszerű lesz, akkor véglegesen bekerülhet a menübe.'
        }
      </div>
      <Sleep />
      {renderNews()}
      {postInModalKey && (
        <Modal
          closeClick={() => {
            setPostInModalKey(undefined)
            router.replace(router.pathname, undefined, { shallow: true })
          }}
        >
          {news ? (
            <NewsEntryContainer
              postKey={postInModalKey}
              setNews={setNews}
              news={news}
              userId={userId}
              newsEntryData={news[postInModalKey]}
            />
          ) : (
            <LoadingIndicator />
          )}
        </Modal>
      )}
    </div>
  )
}
