package view

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"local/dndex/config"
	"local/dndex/storage"
	"local/simpleserve/simpleserve"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
)

func files(_ storage.Graph, w http.ResponseWriter, r *http.Request) error {
	r.URL.Path = strings.TrimPrefix(r.URL.Path, config.New().FilePrefix)
	if len(r.URL.Path) < 2 {
		http.NotFound(w, r)
		return nil
	}
	simpleserve.SetContentTypeIfMedia(w, r)
	switch r.Method {
	case http.MethodGet:
		return filesGet(w, r)
	case http.MethodPost:
		return filesPost(w, r)
	default:
		http.NotFound(w, r)
		return nil
	}
}

func filesGet(w http.ResponseWriter, r *http.Request) error {
	http.ServeFile(w, r, toLocalPath(r.URL.Path))
	return nil
}

func filesPost(w http.ResponseWriter, r *http.Request) error {
	p := toLocalPath(r.URL.Path)
	if err := os.MkdirAll(path.Dir(p), os.ModePerm); err != nil {
		return err
	}
	switch r.URL.Query().Get("direct") {
	case "true":
		return filesPostFromDirectLink(w, r)
	default:
		return filesPostFromUpload(w, r)
	}
}

func filesPostFromDirectLink(w http.ResponseWriter, r *http.Request) error {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	url, err := url.Parse(string(b))
	if err != nil {
		return err
	}
	resp, err := http.Get(url.String())
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status from direct: %v", resp.StatusCode)
	}

	path := toLocalPath(r.URL.Path)
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, io.LimitReader(resp.Body, config.New().MaxFileSize))
	return err
}

func filesPostFromUpload(w http.ResponseWriter, r *http.Request) error {
	p := toLocalPath(r.URL.Path)
	if err := os.MkdirAll(path.Dir(p), os.ModePerm); err != nil {
		return err
	}
	if fi, err := os.Stat(p); err != nil && !os.IsNotExist(err) {
		return err
	} else if err == nil && fi.IsDir() {
		return errors.New("path is a directory")
	}
	f, err := os.Create(p)
	if err != nil {
		return err
	}
	defer f.Close()
	megabyte := 1 << 20
	chunkSize := 10 * megabyte
	r.ParseMultipartForm(int64(chunkSize))
	file, _, err := r.FormFile("file")
	if err != nil {
		return err
	}
	defer file.Close()
	if _, err := io.Copy(f, io.LimitReader(file, config.New().MaxFileSize)); err != nil {
		return err
	}
	return json.NewEncoder(w).Encode(map[string]interface{}{"status": "ok"})
}

func toLocalPath(p string) string {
	return path.Join(config.New().FileRoot, p)
}
