package account

import (
	"net/http"
	"net/url"
	"regexp"
	"strconv"

	"code.nonshy.com/nonshy/website/pkg/config"
	"code.nonshy.com/nonshy/website/pkg/log"
	"code.nonshy.com/nonshy/website/pkg/middleware"
	"code.nonshy.com/nonshy/website/pkg/models"
	"code.nonshy.com/nonshy/website/pkg/session"
	"code.nonshy.com/nonshy/website/pkg/templates"
)

var NotesURLRegexp = regexp.MustCompile(`^/notes/u/([^@]+?)$`)

// User notes page (/notes/u/username)
func UserNotes() http.HandlerFunc {
	tmpl := templates.Must("account/user_notes.html")
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Parse the username out of the URL parameters.
		var username string
		m := NotesURLRegexp.FindStringSubmatch(r.URL.Path)
		if m != nil {
			username = m[1]
		}

		// Find this user.
		user, err := models.FindUser(username)
		if err != nil {
			templates.NotFoundPage(w, r)
			return
		}

		// Get the current user.
		currentUser, err := session.CurrentUser(r)
		if err != nil {
			session.FlashError(w, r, "You must be signed in to view this page.")
			templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String()))
			return
		}

		// Is the site under a Maintenance Mode restriction?
		if middleware.MaintenanceMode(currentUser, w, r) {
			return
		}

		// Banned or disabled? Only admin can view then.
		if user.Status != models.UserStatusActive && !currentUser.IsAdmin {
			templates.NotFoundPage(w, r)
			return
		}

		// Is either one blocking?
		if models.IsBlocking(currentUser.ID, user.ID) && !currentUser.IsAdmin {
			templates.NotFoundPage(w, r)
			return
		}

		// Look up our current note about this person.
		var myNote = models.GetNoteBetweenUsers(currentUser, user)

		// Are we submitting a note?
		if r.Method == http.MethodPost {
			var message = r.FormValue("message")

			// Update & save our note.
			if message == "" {
				// Delete it.
				if err := myNote.Delete(); err != nil && err != models.ErrNoUserNoteToDelete {
					session.FlashError(w, r, "Error deleting your note: %s", err)
				} else if err == nil {
					session.Flash(w, r, "Your note was deleted!")
				}
			} else {
				// Update it.
				myNote.Message = message
				if err := myNote.Save(); err != nil {
					session.FlashError(w, r, "Error saving your note: %s", err)
				} else {
					session.Flash(w, r, "Your notes have been saved!")
				}
			}

			templates.Redirect(w, r.URL.Path)
			return
		}

		// Admin view: paginate their feedback & reports.
		var (
			feedback   = []*models.Feedback{}
			otherNotes = []*models.UserNote{}
			userMap    = models.UserMap{}
			notePager  = &models.Pagination{
				Page:    1,
				PerPage: config.PageSizeAdminUserNotes,
				Sort:    "updated_at desc",
			}
			fbPager = &models.Pagination{
				Page:    1,
				PerPage: config.PageSizeAdminFeedbackNotesPage,
				Sort:    "created_at desc",
			}
		)
		notePager.ParsePage(r)
		fbPager.ParsePage(r)
		if currentUser.IsAdmin {
			// Paginate all notes written about this user.
			if on, err := models.PaginateUserNotes(user, notePager); err != nil {
				session.FlashError(w, r, "Paginating user notes: %s", err)
			} else {
				otherNotes = on
			}

			// Paginate feedback & reports.
			if fb, err := models.PaginateFeedbackAboutUser(user, fbPager); err != nil {
				session.FlashError(w, r, "Paginating feedback on this user: %s", err)
			} else {
				feedback = fb
			}

			// Map user IDs for the Feedback Reply-To line and the Note Givers for the paginated notes.
			var userIDs = []uint64{}
			for _, p := range feedback {
				if p.UserID > 0 {
					userIDs = append(userIDs, p.UserID)
				}
			}
			for _, p := range otherNotes {
				if p.UserID > 0 {
					userIDs = append(userIDs, p.UserID)
				}
			}
			if um, err := models.MapUsers(currentUser, userIDs); err != nil {
				session.FlashError(w, r, "Couldn't map user IDs: %s", err)
			} else {
				userMap = um
			}
		}

		vars := map[string]interface{}{
			"User":        user,
			"PhotoCount":  models.CountPhotosICanSee(user, currentUser),
			"NoteCount":   models.CountNotesAboutUser(currentUser, user),
			"FriendCount": models.CountFriends(user.ID),
			"MyNote":      myNote,

			// Admin concerns.
			"Feedback":      feedback,
			"FeedbackPager": fbPager,
			"OtherNotes":    otherNotes,
			"NotePager":     notePager,
			"UserMap":       userMap,
		}

		if err := tmpl.Execute(w, r, vars); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	})
}

// My user notes page (/notes/me)
func MyNotes() http.HandlerFunc {
	tmpl := templates.Must("account/my_user_notes.html")

	// Whitelist for ordering options.
	var sortWhitelist = []string{
		"updated_at desc",
		"updated_at asc",
		"username desc",
		"username asc",
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Filter parameters.
		var (
			search = r.FormValue("search")
			sort   = r.FormValue("sort")
			sortOK bool
		)

		// Sort options.
		for _, v := range sortWhitelist {
			if sort == v {
				sortOK = true
				break
			}
		}
		if !sortOK {
			sort = sortWhitelist[0]
		}

		// Get the current user.
		currentUser, err := session.CurrentUser(r)
		if err != nil {
			session.FlashError(w, r, "You must be signed in to view this page.")
			templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String()))
			return
		}

		// Is the site under a Maintenance Mode restriction?
		if middleware.MaintenanceMode(currentUser, w, r) {
			return
		}

		// Are we deleting a note?
		if r.Method == http.MethodPost {
			var (
				intent = r.PostFormValue("intent")
				idStr  = r.PostFormValue("id")
			)

			noteID, err := strconv.Atoi(idStr)
			if err != nil {
				session.FlashError(w, r, "Invalid note ID.")
				templates.Redirect(w, r.URL.Path)
				return
			}

			note, err := models.GetNote(uint64(noteID))
			if err != nil {
				session.FlashError(w, r, "Couldn't find that note.")
				templates.Redirect(w, r.URL.Path)
				return
			}

			// Assert it is our note to edit.
			if note.UserID != currentUser.ID {
				session.FlashError(w, r, "That is not your note to edit.")
				templates.Redirect(w, r.URL.Path)
				return
			}

			if intent == "delete" {
				// Delete it!
				if err := note.Delete(); err != nil {
					session.FlashError(w, r, "Error deleting the note: %s.", err)
					templates.Redirect(w, r.URL.Path)
					return
				}
				session.Flash(w, r, "That note has been deleted!")
			}
			templates.Redirect(w, r.URL.Path)
			return
		}

		var (
			pager = &models.Pagination{
				Page:    1,
				PerPage: config.PageSizeAdminUserNotes,
				Sort:    sort,
			}
			userIDs = []uint64{}
		)
		pager.ParsePage(r)

		notes, err := models.PaginateMyUserNotes(currentUser, search, pager)
		if err != nil {
			session.FlashError(w, r, "Error getting your user notes: %s", err)
			templates.Redirect(w, "/")
			return
		}

		// Map user IDs to users.
		for _, note := range notes {
			userIDs = append(userIDs, note.AboutUserID)
		}
		userMap, err := models.MapUsers(currentUser, userIDs)
		if err != nil {
			log.Error("MyUserNotes: couldn't MapUsers: %s", err)
		}

		vars := map[string]interface{}{
			"Notes":   notes,
			"Pager":   pager,
			"UserMap": userMap,

			// Search filters
			"Search": search,
			"Sort":   sort,
		}

		if err := tmpl.Execute(w, r, vars); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	})
}
