package server

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"

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

func (app *App) requireAPI(ctx *gin.Context) bool {
	if app.config.APIEnabled {
		return true
	}
	ctx.JSON(http.StatusForbidden, gin.H{"error": "API access is disabled"})
	return false
}

func (app *App) requireGuestUploads(ctx *gin.Context) bool {
	if app.config.GuestUploadsEnabled {
		return true
	}
	ctx.JSON(http.StatusForbidden, gin.H{"error": "Guest uploads are disabled"})
	return false
}

func (app *App) validateCreateBoxRequest(request *models.CreateBoxRequest) error {
	if request == nil {
		return nil
	}
	if !app.retentionAllowed(request.RetentionKey) {
		return fmt.Errorf("Retention option is not allowed")
	}
	if !app.config.ZipDownloadsEnabled {
		allowZip := false
		request.AllowZip = &allowZip
	}
	if strings.TrimSpace(request.RetentionKey) == boxstore.OneTimeDownloadRetentionKey && !app.config.OneTimeDownloadsEnabled {
		return fmt.Errorf("One-time downloads are disabled")
	}

	totalSize := int64(0)
	for _, file := range request.Files {
		if err := app.validateFileSize(file.Size); err != nil {
			return err
		}
		totalSize += file.Size
	}
	return app.validateBoxSize(totalSize)
}

func (app *App) validateIncomingFile(boxID string, size int64) error {
	if err := app.validateFileSize(size); err != nil {
		return err
	}
	if app.config.GlobalMaxBoxSizeBytes <= 0 {
		return nil
	}

	files, err := boxstore.ListFiles(boxID)
	if err != nil {
		return nil
	}
	totalSize := size
	for _, file := range files {
		totalSize += file.Size
	}
	return app.validateBoxSize(totalSize)
}

func (app *App) validateManifestFileUpload(boxID string, fileID string, size int64) error {
	if err := app.validateFileSize(size); err != nil {
		return err
	}

	manifest, err := boxstore.ReadManifest(boxID)
	if err != nil {
		return app.validateIncomingFile(boxID, size)
	}
	if boxstore.IsExpired(manifest) {
		_ = boxstore.DeleteBox(boxID)
		return fmt.Errorf("Box expired")
	}
	if app.config.GlobalMaxBoxSizeBytes <= 0 {
		return nil
	}
	totalSize := int64(0)
	found := false
	for _, file := range manifest.Files {
		if file.ID == fileID {
			totalSize += size
			found = true
			continue
		}
		totalSize += file.Size
	}
	if !found {
		totalSize += size
	}
	return app.validateBoxSize(totalSize)
}

func (app *App) validateFileSize(size int64) error {
	if size < 0 {
		return fmt.Errorf("File size cannot be negative")
	}
	if app.config.GlobalMaxFileSizeBytes > 0 && size > app.config.GlobalMaxFileSizeBytes {
		return fmt.Errorf("File exceeds the global max file size")
	}
	return nil
}

func (app *App) validateBoxSize(size int64) error {
	if size < 0 {
		return fmt.Errorf("Box size cannot be negative")
	}
	if app.config.GlobalMaxBoxSizeBytes > 0 && size > app.config.GlobalMaxBoxSizeBytes {
		return fmt.Errorf("Box exceeds the global max box size")
	}
	return nil
}

func (app *App) rejectExpiredManifestBox(boxID string) error {
	manifest, err := boxstore.ReadManifest(boxID)
	if err != nil {
		return nil
	}
	if !boxstore.IsExpired(manifest) {
		return nil
	}
	_ = boxstore.DeleteBox(boxID)
	return fmt.Errorf("Box expired")
}

func (app *App) limitRequestBody(ctx *gin.Context) {
	limit := app.maxRequestBodyBytes()
	if limit <= 0 {
		return
	}
	ctx.Request.Body = http.MaxBytesReader(ctx.Writer, ctx.Request.Body, limit)
}

func (app *App) maxRequestBodyBytes() int64 {
	limit := app.config.GlobalMaxBoxSizeBytes
	if limit <= 0 || app.config.GlobalMaxFileSizeBytes > limit {
		limit = app.config.GlobalMaxFileSizeBytes
	}
	if limit <= 0 {
		return 0
	}
	return limit + 10*1024*1024
}
