package emojy

import (
	"embed"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/go-chi/chi/v5"
	"github.com/rs/zerolog/log"
)

//go:embed *.html
var embeddedFS embed.FS

func (p *EmojyPlugin) registerWeb() {
	r := chi.NewRouter()
	r.HandleFunc("/all", p.handleAll)
	r.HandleFunc("/allFiles", p.handleAllFiles)
	r.HandleFunc("/upload", p.handleUpload)
	r.HandleFunc("/file/{name}", p.handleEmojy)
	r.HandleFunc("/stats", p.handlePage("stats.html"))
	r.HandleFunc("/list", p.handlePage("list.html"))
	r.HandleFunc("/new", p.handlePage("upload.html"))
	r.HandleFunc("/", p.handleIndex)
	p.b.RegisterWebName(r, "/emojy", "Emojys")
}

func (p *EmojyPlugin) handleIndex(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, "/emojy/stats", http.StatusPermanentRedirect)
}

func (p *EmojyPlugin) handlePage(file string) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		index, _ := embeddedFS.ReadFile(file)
		w.Write(index)
	}
}

func (p *EmojyPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
	threshold := p.c.GetInt("emojy.statthreshold", 1)
	emojy, err := p.allCounts(threshold)
	if err != nil {
		w.WriteHeader(500)
		log.Error().Err(err).Msgf("handleAll")
		out, _ := json.Marshal(struct{ err error }{err})
		w.Write(out)
	}
	out, _ := json.Marshal(emojy)
	w.Write(out)
}

func (p *EmojyPlugin) handleAllFiles(w http.ResponseWriter, r *http.Request) {
	_, urlMap, err := AllFiles(p.emojyPath, p.baseURL)
	if err != nil {
		w.WriteHeader(500)
		out, _ := json.Marshal(struct{ err error }{err})
		w.Write(out)
		return
	}
	enc := json.NewEncoder(w)
	err = enc.Encode(urlMap)
	if err != nil {
		w.WriteHeader(500)
		out, _ := json.Marshal(struct{ err error }{err})
		w.Write(out)
	}
}

func (p *EmojyPlugin) handleUpload(w http.ResponseWriter, r *http.Request) {
	enc := json.NewEncoder(w)
	newFilePath, err := p.FileSave(r)
	if err != nil {
		log.Error().Err(err).Msgf("could not upload file")
		w.WriteHeader(500)
		enc.Encode(struct{ err error }{fmt.Errorf("file not saved: %s", err)})
		return
	}
	log.Debug().Msgf("uploaded file to %s", newFilePath)
	w.WriteHeader(200)
	enc.Encode(struct{ file string }{newFilePath})

}

func (p *EmojyPlugin) FileSave(r *http.Request) (string, error) {
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		if err != http.ErrNotMultipart {
			return "", err
		}
		if err := r.ParseForm(); err != nil {
			return "", err
		}
	}
	if r.MultipartForm == nil || len(r.MultipartForm.File) == 0 {
		return "", fmt.Errorf("no files")
	}

	password := r.Form.Get("password")
	if password != p.b.GetPassword() {
		return "", fmt.Errorf("incorrect password")
	}

	for _, fileHeaders := range r.MultipartForm.File {
		for _, fileHeader := range fileHeaders {
			body, err := fileHeader.Open()
			if err != nil {
				return "", fmt.Errorf("error opening part %q: %s", fileHeader.Filename, err)
			}
			emojyFileName := fileHeader.Filename
			emojyName := strings.TrimSuffix(emojyFileName, filepath.Ext(emojyFileName))
			if ok, _, _, _ := p.isKnownEmojy(emojyName); ok {
				return "", fmt.Errorf("emojy already exists")
			}
			contentType := fileHeader.Header.Get("Content-Type")
			if !strings.HasPrefix(contentType, "image") {
				return "", fmt.Errorf("incorrect mime type - given: %s", contentType)
			}
			fullPath := filepath.Clean(filepath.Join(p.emojyPath, emojyFileName))
			_ = os.MkdirAll(p.emojyPath, os.ModePerm)
			log.Debug().Msgf("trying to create/open file: %s", fullPath)
			file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, os.ModePerm)
			if err != nil {
				return "", err
			}
			defer file.Close()
			_, err = io.Copy(file, body)
			if err != nil {
				return "", err
			}
			return emojyFileName, nil
		}
	}
	return "", fmt.Errorf("did not find file")
}

func (p *EmojyPlugin) handleEmojy(w http.ResponseWriter, r *http.Request) {
	fname := chi.URLParam(r, "name")
	contents, err := ioutil.ReadFile(path.Join(p.emojyPath, fname))
	if err != nil {
		w.WriteHeader(404)
		out, _ := json.Marshal(struct{ err error }{err})
		w.Write(out)
	}
	w.Write(contents)
}