package api

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"

	"github.com/davidbyttow/govips/v2/vips"
	log "github.com/sirupsen/logrus"
	"reichard.io/imagini/graph/model"
)

/**
 * Responsible for serving up static images / videos
 **/
func (api *API) mediaHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	if path.Dir(r.URL.Path) != "/media" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	// Acquire Width & Height Parameters
	width, _ := strconv.Atoi(r.URL.Query().Get("width"))

	// Pull out userID
	authContext := r.Context().Value("auth").(*model.AuthContext)
	rawUserID, _ := (*authContext.AccessToken).Get("sub")
	userID := rawUserID.(string)

	// TODO: Derive Cache & Width Location
	// Derive Path
	fileName := path.Base(r.URL.Path)
	folderPath := path.Join("/" + api.Config.DataPath + "/media/" + userID)
	mediaPath := path.Join(folderPath + "/" + fileName)

	// Check if File Exists
	_, err := os.Stat(mediaPath)
	if os.IsNotExist(err) {
		log.Warn("[media] Image does not exist on disk")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	mediaFile, err := resizeAndConvertImage(mediaPath, width)
	if err != nil {
		log.Warn("[media] Image conversion failed:", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// TODO: Cache mediaFile

	http.ServeContent(w, r, fileName, time.Time{}, bytes.NewReader(mediaFile))
}

func resizeAndConvertImage(path string, desiredWidth int) ([]byte, error) {
	inputImage, err := vips.NewImageFromFile(path)
	if err != nil {
		return nil, errors.New("[media] Unable to read image")
	}

	// If we're viewing full image, we want full quality
	desiredQuality := 100

	// Do we need to scale?
	if desiredWidth != 0 && inputImage.Width() > desiredWidth {
		desiredQuality = 50
		desiredScale := float64(desiredWidth) / float64(inputImage.Width())
		err := inputImage.Resize(desiredScale, vips.KernelLanczos3)
		if err != nil {
			return nil, errors.New("[media] Unable to resize")
		}
	} else if inputImage.Format() == vips.ImageTypeJPEG {
		// Return raw file
		return ioutil.ReadFile(path)
	}

	// Return non-converted but scaled image
	if inputImage.Format() == vips.ImageTypeJPEG {
		imageBytes, _, err := inputImage.ExportNative()
		return imageBytes, err
	}

	// Convert
	ep := vips.NewJpegExportParams()
	ep.Quality = desiredQuality
	imageBytes, _, err := inputImage.ExportJpeg(ep)
	return imageBytes, err
}
