package photo

import (
	"net/http"
	"regexp"

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

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

// UserPhotos controller (/photo/u/:username) to view a user's gallery or manage if it's yourself.
func UserPhotos() http.HandlerFunc {
	tmpl := templates.Must("photo/gallery.html")

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

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Query params.
		var (
			viewStyle = r.FormValue("view") // cards (default), full

			// Search filters.
			filterExplicit   = r.FormValue("explicit")
			filterVisibility = r.FormValue("visibility")
			sort             = r.FormValue("sort")
			sortOK           bool
		)

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

		// Defaults.
		if viewStyle != "full" {
			viewStyle = "cards"
		}

		// Parse the username out of the URL parameters.
		var username string
		m := UserPhotosRegexp.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
		}

		// Load the current user in case they are viewing their own page.
		currentUser, err := session.CurrentUser(r)
		if err != nil {
			session.FlashError(w, r, "Unexpected error: couldn't get CurrentUser")
		}
		var (
			areFriends  = models.AreFriends(user.ID, currentUser.ID)
			isPrivate   = user.Visibility == models.UserVisibilityPrivate && !areFriends
			isOwnPhotos = currentUser.ID == user.ID
			isShy       = currentUser.IsShy()
			isShyFrom   = !isOwnPhotos && (currentUser.IsShyFrom(user) || (isShy && !areFriends))
		)

		// Bail early if we are shy from this user.
		if isShy && isShyFrom {
			var vars = map[string]interface{}{
				"IsOwnPhotos": currentUser.ID == user.ID,
				"IsShyUser":   isShy,
				"IsShyFrom":   isShyFrom,
				// "IsMyPrivateUnlockedFor": isGranted, // have WE granted THIS USER to see our private pics?
				// "AreWeGrantedPrivate":    isGrantee, // have THEY granted US private photo access.
				"User":       user,
				"Photos":     []*models.Photo{},
				"PhotoCount": models.CountPhotos(user.ID),
				"Pager":      &models.Pagination{},
			}

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

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

		// Is this user private and we're not friends?
		if isPrivate && !currentUser.IsAdmin && !isOwnPhotos {
			session.FlashError(w, r, "This user's profile page and photo gallery are private.")
			templates.Redirect(w, "/u/"+user.Username)
			return
		}

		// Has this user granted access to see their privates?
		var (
			isGrantee = models.IsPrivateUnlocked(user.ID, currentUser.ID) // THEY have granted US access
			isGranted = models.IsPrivateUnlocked(currentUser.ID, user.ID) // WE have granted THEM access
		)

		// What set of visibilities to query?
		visibility := []models.PhotoVisibility{models.PhotoPublic}
		if isOwnPhotos || isGrantee || currentUser.HasAdminScope(config.ScopePhotoModerator) {
			visibility = append(visibility, models.PhotoPrivate)
		}
		if isOwnPhotos || areFriends || currentUser.HasAdminScope(config.ScopePhotoModerator) {
			visibility = append(visibility, models.PhotoFriends)
		}

		// Inner circle photos.
		if currentUser.IsInnerCircle() {
			visibility = append(visibility, models.PhotoInnerCircle)
		}

		// If we are Filtering by Visibility, ensure the target visibility is accessible to us.
		if filterVisibility != "" {
			var isOK bool
			for _, allowed := range visibility {
				if allowed == models.PhotoVisibility(filterVisibility) {
					isOK = true
					break
				}
			}

			// If the filter is within the set we are allowed to see, update the set.
			if isOK {
				visibility = []models.PhotoVisibility{models.PhotoVisibility(filterVisibility)}
			} else {
				session.FlashError(w, r, "Could not filter pictures by that visibility setting: it is not available for you.")
				visibility = []models.PhotoVisibility{models.PhotoNotAvailable}
			}
		}

		// Explicit photo filter? The default ("") will defer to the user's Explicit opt-in.
		if filterExplicit == "" {
			// If the viewer does not opt-in to explicit AND is not looking at their own gallery,
			// then default the explicit filter to "do not show explicit"
			if !currentUser.Explicit && !isOwnPhotos {
				filterExplicit = "false"
			}
		}

		// Get the page of photos.
		pager := &models.Pagination{
			Page:    1,
			PerPage: config.PageSizeUserGallery,
			Sort:    sort,
		}
		pager.ParsePage(r)
		photos, err := models.PaginateUserPhotos(user.ID, models.UserGallery{
			Explicit:   filterExplicit,
			Visibility: visibility,
		}, pager)
		if err != nil {
			log.Error("PaginateUserPhotos(%s): %s", user.Username, err)
		}

		// Get the count of explicit photos if we are not viewing explicit photos.
		var explicitCount int64
		if filterExplicit == "false" {
			explicitCount, _ = models.CountExplicitPhotos(user.ID, visibility)
		}

		// Get Likes information about these photos.
		var photoIDs = []uint64{}
		for _, p := range photos {
			photoIDs = append(photoIDs, p.ID)
		}
		likeMap := models.MapLikes(currentUser, "photos", photoIDs)
		commentMap := models.MapCommentCounts("photos", photoIDs)

		var vars = map[string]interface{}{
			"IsOwnPhotos":                    currentUser.ID == user.ID,
			"IsShyUser":                      isShy,
			"IsShyFrom":                      isShyFrom,
			"IsMyPrivateUnlockedFor":         isGranted, // have WE granted THIS USER to see our private pics?
			"AreWeGrantedPrivate":            isGrantee, // have THEY granted US private photo access.
			"AreFriends":                     areFriends,
			"User":                           user,
			"Photos":                         photos,
			"PhotoCount":                     models.CountPhotosICanSee(user, currentUser),
			"NoteCount":                      models.CountNotesAboutUser(currentUser, user),
			"FriendCount":                    models.CountFriends(user.ID),
			"PublicPhotoCount":               models.CountPublicPhotos(user.ID),
			"InnerCircleMinimumPublicPhotos": config.InnerCircleMinimumPublicPhotos,
			"Pager":                          pager,
			"LikeMap":                        likeMap,
			"CommentMap":                     commentMap,
			"ViewStyle":                      viewStyle,
			"ExplicitCount":                  explicitCount,

			// Search filters
			"Sort":             sort,
			"FilterExplicit":   filterExplicit,
			"FilterVisibility": filterVisibility,
		}

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