package forum

import (
	"fmt"
	"net/http"
	"strconv"

	"git.kirsle.net/apps/gosocial/pkg/markdown"
	"git.kirsle.net/apps/gosocial/pkg/models"
	"git.kirsle.net/apps/gosocial/pkg/session"
	"git.kirsle.net/apps/gosocial/pkg/templates"
)

// NewPost view.
func NewPost() http.HandlerFunc {
	tmpl := templates.Must("forum/new_post.html")
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Query params.
		var (
			fragment       = r.FormValue("to")                     // forum to (new post)
			toThreadID     = r.FormValue("thread")                 // add reply to a thread ID
			quoteCommentID = r.FormValue("quote")                  // add reply to thread while quoting a comment
			editCommentID  = r.FormValue("edit")                   // edit your comment
			intent         = r.FormValue("intent")                 // preview or submit
			title          = r.FormValue("title")                  // for new forum post only
			message        = r.PostFormValue("message")            // comment body
			isPinned       = r.PostFormValue("pinned") == "true"   // owners or admins only
			isExplicit     = r.PostFormValue("explicit") == "true" // for thread only
			isNoReply      = r.PostFormValue("noreply") == "true"  // for thread only
			isDelete       = r.FormValue("delete") == "true"       // delete comment (along with edit=$id)
			forum          *models.Forum
			thread         *models.Thread  // if replying to a thread
			comment        *models.Comment // if editing a comment

			// If we are modifying a comment (post) and it's the OG post of the
			// thread, we show and accept the thread settings to be updated as
			// well (pinned, explicit, noreply)
			isOriginalComment bool
		)

		// Get the current user.
		currentUser, err := session.CurrentUser(r)
		if err != nil {
			session.FlashError(w, r, "Couldn't get current user: %s", err)
			templates.Redirect(w, "/")
			return
		}

		// Look up the forum itself.
		if found, err := models.ForumByFragment(fragment); err != nil {
			session.FlashError(w, r, "Couldn't post to forum %s: not found.", fragment)
			templates.Redirect(w, "/forum")
			return
		} else {
			forum = found
		}

		// Are we manipulating a reply to an existing thread?
		if len(toThreadID) > 0 {
			if i, err := strconv.Atoi(toThreadID); err == nil {
				if found, err := models.GetThread(uint64(i)); err != nil {
					session.FlashError(w, r, "Couldn't find that thread ID!")
					templates.Redirect(w, fmt.Sprintf("/f/%s", forum.Fragment))
					return
				} else {
					thread = found
				}
			}
		}

		// Are we pre-filling the message with a quotation of an existing comment?
		if len(quoteCommentID) > 0 {
			if i, err := strconv.Atoi(quoteCommentID); err == nil {
				if comment, err := models.GetComment(uint64(i)); err == nil {
					message = markdown.Quotify(comment.Message) + "\n\n"
				}
			}
		}

		// Are we editing or deleting our comment?
		if len(editCommentID) > 0 {
			if i, err := strconv.Atoi(editCommentID); err == nil {
				if found, err := models.GetComment(uint64(i)); err == nil {
					comment = found

					// Verify that it is indeed OUR comment.
					if currentUser.ID != comment.UserID && !currentUser.IsAdmin {
						templates.ForbiddenPage(w, r)
						return
					}

					// Initialize the form w/ the content of this message.
					if r.Method == http.MethodGet {
						message = comment.Message
					}

					// Is this the OG thread of the post?
					if thread.CommentID == comment.ID {
						isOriginalComment = true

						// Restore the checkbox option form values from thread settings.
						if r.Method == http.MethodGet {
							isPinned = thread.Pinned
							isExplicit = thread.Explicit
							isNoReply = thread.NoReply
						}
					}

					// Are we DELETING this comment?
					if isDelete {
						if err := thread.DeleteReply(comment); err != nil {
							session.FlashError(w, r, "Error deleting your post: %s", err)
						} else {
							session.Flash(w, r, "Your post has been deleted.")
						}
						templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
						return
					}
				} else {
					// Comment not found - show the Forbidden page anyway.
					templates.ForbiddenPage(w, r)
					return
				}
			} else {
				templates.NotFoundPage(w, r)
				return
			}
		}

		// Submitting the form.
		if r.Method == http.MethodPost {
			// Default intent is preview unless told to submit.
			if intent == "submit" {
				// Are we modifying an existing comment?
				if comment != nil {
					comment.Message = message

					// Can we update the thread props?
					if isOriginalComment {
						thread.Pinned = isPinned
						thread.Explicit = isExplicit
						thread.NoReply = isNoReply
						if err := thread.Save(); err != nil {
							session.FlashError(w, r, "Couldn't save thread properties: %s", err)
						}
					}

					if err := comment.Save(); err != nil {
						session.FlashError(w, r, "Couldn't save comment: %s", err)
					} else {
						session.Flash(w, r, "Comment updated!")
					}
					templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
					return
				}

				// Are we replying to an existing thread?
				if thread != nil {
					if _, err := thread.Reply(currentUser, message); err != nil {
						session.FlashError(w, r, "Couldn't add reply to thread: %s", err)
					} else {
						session.Flash(w, r, "Reply added to the thread!")
					}
					templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
					return
				}

				// Create a new thread?
				if thread, err := models.CreateThread(
					currentUser,
					forum.ID,
					title,
					message,
					isPinned,
					isExplicit,
					isNoReply,
				); err != nil {
					session.FlashError(w, r, "Couldn't create thread: %s", err)
				} else {
					session.Flash(w, r, "Thread created!")
					templates.Redirect(w, fmt.Sprintf("/forum/thread/%d", thread.ID))
					return
				}
			}
		}

		var vars = map[string]interface{}{
			"Forum":              forum,
			"Thread":             thread,
			"Intent":             intent,
			"PostTitle":          title,
			"EditCommentID":      editCommentID,
			"EditThreadSettings": isOriginalComment,
			"Message":            message,

			// Thread settings (for editing the original comment esp.)
			"IsPinned":   isPinned,
			"IsExplicit": isExplicit,
			"IsNoReply":  isNoReply,
		}
		if err := tmpl.Execute(w, r, vars); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	})
}
