package server

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"

	"warpbox/lib/boxstore"
	"warpbox/lib/models"
)

const boxAuthCookiePrefix = "warpbox_box_"

func handleBoxLogin(ctx *gin.Context) {
	boxID := ctx.Param("id")
	if !boxstore.ValidBoxID(boxID) {
		ctx.String(http.StatusBadRequest, "Invalid box id")
		return
	}

	manifest, err := boxstore.ReadManifest(boxID)
	if err != nil {
		ctx.String(http.StatusNotFound, "Box not found")
		return
	}

	if boxstore.IsExpired(manifest) {
		boxstore.DeleteBox(boxID)
		ctx.String(http.StatusGone, "Box expired")
		return
	}

	if !boxstore.IsPasswordProtected(manifest) || isBoxAuthorized(ctx, boxID, manifest) {
		ctx.Redirect(http.StatusSeeOther, "/box/"+boxID)
		return
	}

	renderBoxLogin(ctx, boxID, "")
}

func handleBoxLoginPost(ctx *gin.Context) {
	boxID := ctx.Param("id")
	if !boxstore.ValidBoxID(boxID) {
		ctx.String(http.StatusBadRequest, "Invalid box id")
		return
	}

	manifest, err := boxstore.ReadManifest(boxID)
	if err != nil {
		ctx.String(http.StatusNotFound, "Box not found")
		return
	}

	if boxstore.IsExpired(manifest) {
		boxstore.DeleteBox(boxID)
		ctx.String(http.StatusGone, "Box expired")
		return
	}

	if !boxstore.VerifyPassword(manifest, ctx.PostForm("password")) {
		renderBoxLogin(ctx, boxID, "The password was not accepted.")
		return
	}

	maxAge := 24 * 60 * 60
	if !manifest.ExpiresAt.IsZero() {
		seconds := int(time.Until(manifest.ExpiresAt).Seconds())
		if seconds > 0 {
			maxAge = seconds
		}
	}

	ctx.SetCookie(boxAuthCookieName(boxID), manifest.AuthToken, maxAge, "/box/"+boxID, "", false, true)
	ctx.Redirect(http.StatusSeeOther, "/box/"+boxID)
}
func (app *App) authorizeBoxRequest(ctx *gin.Context, boxID string, wantsHTML bool) (models.BoxManifest, bool, bool) {
	manifest, err := boxstore.ReadManifest(boxID)
	if err != nil {
		return models.BoxManifest{}, false, true
	}

	if boxstore.IsExpired(manifest) {
		boxstore.DeleteBox(boxID)
		if wantsHTML {
			ctx.String(http.StatusGone, "Box expired")
		} else {
			ctx.JSON(http.StatusGone, gin.H{"error": "Box expired"})
		}
		return manifest, true, false
	}

	if manifest.OneTimeDownload && manifest.Consumed {
		if wantsHTML {
			ctx.String(http.StatusGone, "Box already consumed")
		} else {
			ctx.JSON(http.StatusGone, gin.H{"error": "Box already consumed"})
		}
		return manifest, true, false
	}

	if boxstore.IsPasswordProtected(manifest) && !isBoxAuthorized(ctx, boxID, manifest) {
		if wantsHTML {
			ctx.Redirect(http.StatusSeeOther, "/box/"+boxID+"/login")
		} else {
			ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Password required"})
		}
		return manifest, true, false
	}

	if app.config.RenewOnAccessEnabled {
		if renewed, err := boxstore.RenewManifest(boxID, manifest.RetentionSecs); err == nil {
			manifest = renewed
		}
	}

	return manifest, true, true
}

func isBoxAuthorized(ctx *gin.Context, boxID string, manifest models.BoxManifest) bool {
	token, err := ctx.Cookie(boxAuthCookieName(boxID))
	return err == nil && boxstore.VerifyAuthToken(manifest, token)
}

func boxAuthCookieName(boxID string) string {
	return boxAuthCookiePrefix + boxID
}

func renderBoxLogin(ctx *gin.Context, boxID string, errorMessage string) {
	ctx.HTML(http.StatusOK, "box_login.html", gin.H{
		"BoxID":        boxID,
		"BoxUser":      "WarpBox\\" + boxID,
		"ErrorMessage": errorMessage,
	})
}
