// Copyright (C) 2022 Marius Schellenberger

package srv

import (
	"io"
	"net/http"
	"path"
	"strings"

	"cachefs/pkg/fs"

	"github.com/go-logr/logr"
)

type CacheFS struct {
	fs *fs.FS
}

func (cfs *CacheFS) Open(name string) (http.File, error) {
	return cfs.fs.OpenDst(name)
}

type CacheServer struct {
	log logr.Logger
	fs  *fs.FS
	h   http.Handler
}

func NewCacheServer(fs *fs.FS, log logr.Logger) http.Handler {
	return &CacheServer{log, fs, http.FileServer(&CacheFS{fs})}
}

func (cs *CacheServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s := &statusInterceptor{w: w}
	w = s
	if !skipLog(r.RequestURI) {
		defer func() {
			if s.Status() == http.StatusPartialContent {
				return
			}
			cs.log.Info("access",
				"client", r.RemoteAddr,
				"method", r.Method,
				"status", s.Status(),
				"uri", r.RequestURI,
			)
		}()
	}
	upath := r.URL.Path
	if !strings.HasPrefix(upath, "/") {
		upath = "/" + upath
		r.URL.Path = upath
	}
	p := path.Clean(upath)
	option := r.FormValue("o")
	d, err := cs.fs.StatDst(p)
	if err != nil {
		msg, code := toHTTPError(err)
		http.Error(w, msg, code)
		return
	}
	h := w.Header()
	if option == "v" {
		h.Set(csp, videoCSP)
		err = video.Execute(w, data{Paths: dirContents{Base: r.URL.Path}})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}

	if !d.IsDir() {
		cs.h.ServeHTTP(w, r)
		return
	}

	i := &responseInterceptor{w: w}
	r.Header.Del("If-Modified-Since")
	r.Header.Del("Cache-Control")
	cs.h.ServeHTTP(i, r)

	h.Del("Last-Modified")

	paths, err := i.GetPaths(p, cs.fs, true)
	if err == io.EOF {
		w.WriteHeader(i.Status())
		return
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	h.Set(csp, indexCSP)

	w.WriteHeader(i.Status())
	err = cache.Execute(w, data{Paths: paths})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}
